#!/bin/sh

# Part of passwordless cryptofs setup in Debian Etch.
# See: http://wejn.org/how-to-make-passwordless-cryptsetup.html
# Author: Wejn <wejn at box dot cz>
#
# Updated by Rodolfo Garcia (kix) <kix at kix dot com>
# For multiple partitions
# http://www.kix.es/
#
# Updated by TJ <linux@tjworld.net> 7 July 2008
# For use with Ubuntu Hardy, usplash, automatic detection of USB devices,
# detection and examination of *all* partitions on the device (not just partition #1), 
# automatic detection of partition type, refactored, commented, debugging code.
#
# Updated by Hendrik van Antwerpen <hendrik at van-antwerpen dot net> 3 Sept 2008
# For encrypted key device support, also added stty support for not
# showing your password in console mode.

# define counter-intuitive shell logic values (based on /bin/true & /bin/false)
# NB. use FALSE only to *set* something to false, but don't test for
# equality, because a program might return any non-zero on error
TRUE=0
FALSE=1

# set DEBUG=$TRUE to display debug messages, DEBUG=$FALSE to be quiet
DEBUG=$FALSE

# is usplash available? default false
USPLASH=$FALSE
# test for outfifo from Ubuntu Hardy cryptroot script, the second test
# alone proves not completely reliable.
if [ -p /dev/.initramfs/usplash_outfifo -a -x /sbin/usplash_write ]; then
	# use innocuous command to determine if usplash is running
	# usplash_write will return exit-code 1 if usplash isn't running
	# need to set a flag to tell usplash_write to report no usplash
	FAIL_NO_USPLASH=1
	# enable verbose messages (required to display messages if kernel boot option "quiet" is enabled
	/sbin/usplash_write "VERBOSE on"
	if [ $? -eq $TRUE ]; then
		# usplash is running
		USPLASH=$TRUE
		/sbin/usplash_write "CLEAR"
	fi
fi

# is stty available? default false
STTY=$FALSE
STTYCMD=false
# check for stty executable
if [ -x /bin/stty ]; then
	STTY=$TRUE
	STTYCMD=/bin/stty
elif [ `(busybox stty >/dev/null 2>&1; echo $?)` -eq $TRUE ]; then
	STTY=$TRUE
	STTYCMD="busybox stty"
fi

# print message to usplash or stderr
# usage: msg <command> "message" [switch]
# command: TEXT | STATUS | SUCCESS | FAILURE | CLEAR (see 'man usplash_write' for all commands)
# switch : switch used for echo to stderr (ignored for usplash)
# when using usplash the command will cause "message" to be
# printed according to the usplash <command> definition.
# using the switch -n will allow echo to write multiple messages
# to the same line
msg ()
{
	if [ $# -gt 0 ]; then
		# handle multi-line messages
		echo $2 | while read LINE; do
			if [ $USPLASH -eq $TRUE ]; then
				# use usplash
				/sbin/usplash_write "$1 $LINE"		
			else
				# use stderr for all messages
				echo $3 "$2" >&2
			fi
		done
	fi
}

dbg ()
{
	if [ $DEBUG -eq $TRUE ]; then
		msg "$@"
	fi
}

# read password from console or with usplash
# usage: readpass "prompt"
readpass ()
{
	if [ $# -gt 0 ]; then
		if [ $USPLASH -eq $TRUE ]; then
			usplash_write "INPUTQUIET $1: "
			PASS="$(cat /dev/.initramfs/usplash_outfifo)"
		else
			[ $STTY -ne $TRUE ] && msg TEXT "WARNING stty not found, password will be visible"
			echo -n "$1" >&2
			$STTYCMD -echo
			read -r PASS </dev/console >/dev/null
			[ $STTY -eq $TRUE ] && echo >&2
			$STTYCMD echo
		fi
	fi
	echo -n "$PASS"
}

dbg STATUS "Executing crypto-usb-key.sh ..."

# flag tracking key-file availability
OPENED=$FALSE

# temporary mount path for USB key
MD=/tmp-usb-mount

if [ "x$1" = "x" -o "x$1" = "xnone" ]; then
	# default key-file on the USB disk
	KEYFILE=.key
else
	KEYFILE=$1
fi

# If the file already exists use it.
# This is useful where an encrypted volume contains keyfile(s) for later
# volumes and is now mounted and accessible
if [ -f $KEYFILE ]; then
	dbg TEXT "Found $KEYFILE"
	cat $KEYFILE
	OPENED=$TRUE
	DEV="existing mount"
	LABEL=""
else
	# Is the USB driver loaded?
	cat /proc/modules | busybox grep usb_storage >/dev/null 2>&1
	USBLOAD=0$?
	if [ $USBLOAD -gt 0 ]; then
		dbg TEXT "Loading driver 'usb_storage'"
		modprobe usb_storage >/dev/null 2>&1
	fi

	# give the system time to settle and open the USB devices
	sleep 7

	# Are there any SCSI block devices?
	ls -d /sys/block/sd* >/dev/null 2>&1
	SBD=$?

	if [ $SBD -eq $TRUE ]; then
		mkdir -p $MD
		dbg TEXT "Trying to get key-file '$KEYFILE' ..."
		for SFS in /sys/block/sd*/sd??; do
			dbg TEXT "Examining $SFS" -n
			# Is the device removable?
			REMOVABLE=0`cat ${SFS}/../removable`
			dbg TEXT ", REMOVABLE=$REMOVABLE" -n
			if [ $REMOVABLE -eq 1 -a -f $SFS/dev ]; then
				dbg TEXT ", *possible key device*" -n
				DEV=`busybox basename $SFS`
				# Check if key device itself is encrypted
				/sbin/cryptsetup isLuks /dev/${DEV} >/dev/null 2>&1
				ENCRYPTED=0$?
				# Open crypted partition and prepare for mount
				if [ $ENCRYPTED -eq $TRUE ]; then
					dbg TEXT ", encrypted device" -n
					# Use vol_id to determine label
					LABEL="`/lib/udev/vol_id /dev/${DEV} | busybox sed -n 's/.*ID_FS_LABEL_SAFE=\(.*\)/\1/p'`"
					dbg TEXT ", label $LABEL" -n
					TRIES=3
					DECRYPTED=$FALSE
					while [ $TRIES -gt 0 -a $DECRYPTED -ne $TRUE ]; do
						TRIES=$(($TRIES-1))
						PASS="`readpass \"Enter LUKS password for key device ${DEV} (${LABEL}) (or empty to skip): \"`"
						if [ -z "$PASS" ]; then
							dbg TEXT ", device skipped" -n
							break
						fi
						echo $PASS | /sbin/cryptsetup luksOpen /dev/${DEV} bootkey >/dev/null 2>&1
						DECRYPTED=0$?
					done
					# If open failed, skip this device
					if [ $DECRYPTED -ne $TRUE ]; then
						dbg TEXT "decrypting device failed" -n
						break
					fi
					# Decrypted device to use
					DEV=mapper/bootkey
				fi
				dbg TEXT ", device $DEV" -n
				# Use vol_id to determine label
				LABEL="`/lib/udev/vol_id /dev/${DEV} | busybox sed -n 's/.*ID_FS_LABEL_SAFE=\(.*\)/\1/p'`"
				dbg TEXT ", label $LABEL" -n
				# Use vol_id to determine fstype
				FSTYPE="`/lib/udev/vol_id /dev/${DEV} | busybox sed -n 's/.*ID_FS_TYPE=\(.*\)/\1/p'`"
				dbg TEXT ", fstype $FSTYPE" -n
				# Is the file-system driver loaded?
				cat /proc/modules | busybox grep $FSTYPE >/dev/null 2>&1
				FSLOAD=0$?
				if [ $FSLOAD -gt 0 ]; then
					dbg TEXT ", loading driver for $FSTYPE" -n
					# load the correct file-system driver
					modprobe $FSTYPE >/dev/null 2>&1
				fi
				dbg TEXT ", mounting /dev/$DEV on $MD" -n
				mount /dev/${DEV} $MD -t $FSTYPE -o ro >/dev/null 2>&1
				dbg TEXT ", (`mount | busybox grep $DEV`)" -n
				if [ -f $MD/$KEYFILE ]; then
					dbg TEXT ", found $MD/$KEYFILE" -n
					cat $MD/$KEYFILE
					OPENED=$TRUE
				fi
				dbg TEXT ", umount $MD" -n
				umount $MD >/dev/null 2>&1
				# Close encrypted key device
				if [ $ENCRYPTED -eq $TRUE -a $DECRYPTED -eq $TRUE ]; then
					dbg TEXT ", closing encrypted device" -n
					/sbin/cryptsetup luksClose bootkey >/dev/null 2>&1
				fi
				dbg TEXT ", done\n\n" -n
				if [ $OPENED -eq $TRUE ]; then
					break
				fi
			else
				dbg TEXT ", device `busybox basename $SFS` ignored" -n
			fi
			dbg CLEAR ""
		done
	fi
fi

# clear existing usplash text and status messages
[ $USPLASH -eq $TRUE ] && msg STATUS "                               " && msg CLEAR ""

if [ $OPENED -ne $TRUE ]; then
	msg TEXT "FAILED to find suitable USB key-file ..."
	readpass "Try to enter the LUKS password: "
else
	msg TEXT "Success loading key-file from $SFS ($LABEL)"
fi

# 
[ $USPLASH -eq $TRUE ] && /sbin/usplash_write "VERBOSE default"

