#!/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.
#
# Update by Timothy Pearson <kb9vqf@pearsoncomputing.net> 8/28/2008
# Modified for use with SmartCard script instead of USB key
#
# Updated by Timothy Pearson <kb9vqf@pearsoncomputing.net> 4/19/2010
# Added Plymouth detection and support
#
# Updated by Timothy Pearson <kb9vqf@pearsoncomputing.net> 9/15/2015
# Rewrite to use on-card RSA encryption
#
# Updated by Timothy Pearson <kb9vqf@pearsoncomputing.net> 6/02/2020
# Use central variable for PKCS library location
#
# Updated by Timothy Pearson <kb9vqf@pearsoncomputing.net> 6/03/2020
# Fix random pool start detection on new kernels

# define counter-intuitive shell logic values (based on /bin/true & /bin/false)
TRUE=0
FALSE=1

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

# Fix the aggressive usplash timeout
if [ -x /sbin/usplash_write ]; then
	/sbin/usplash_write "TIMEOUT 180" || true
fi

# Find plymouth
PLYDIR=/bin/plymouth

# Determine multiarch directory
ARCHDIR=$(ldd $SHELL | grep libc\.so |\
          sed -e "s|^.*=> \(.*\)/[^/]*$|\1|" -e "s|^/usr||" -e "s|^/||")
if [ -z "$ARCHDIR" ] && [ -n "$HOSTTYPE" ] && [ -n "$OSTYPE" ]; then
  ARCHDIR=$HOSTTYPE-$OSTYPE
fi
if [ -z "$ARCHDIR" ]; then
  ARCHDIR=$(uname -i)
  if [ "$ARCHDIR" = "unknown" ]; then
    ARCHDIR=$(uname -m)
  fi
  if [ "$ARCHDIR" = "ppc64le" ]; then
    ARCHDIR="powerpc64"
  fi
  if [ "$ARCHDIR" = "ppc64el" ]; then
    ARCHDIR="powerpc64"
  fi
  ARCHDIR="*${ARCHDIR}*"
fi

# Set system PKCS library path
SYSTEM_PKCS_LIBRARY=opensc-pkcs11.so
for x in /${ARCHDIR} /lib /usr/${ARCHDIR} /usr/lib; do
	if [ -e $x/$SYSTEM_PKCS_LIBRARY ]; then
		SYSTEM_PKCS_LIBRARY=$(echo $x/$SYSTEM_PKCS_LIBRARY)
		break
	fi
done

# 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 [ -p /dev/.initramfs/usplash_outfifo ] && [ -x /sbin/usplash_write ]; then
# 		usplash_write "TEXT-URGENT $@"
# 	else
# 		echo "$@" >&2
# 	fi
# 	return 0
# }

msg ()
{
	HAS_PLYMOUTH=0
	if [ -e $PLYDIR ]; then
		plymouth --ping
		if [ $? -eq 0 ]; then
			HAS_PLYMOUTH=1
		fi
	fi
	if [ $HAS_PLYMOUTH -eq 1 ]; then
		plymouth message --text="$@"
	else
		if [ -p /dev/.initramfs/usplash_outfifo ] && [ -x /sbin/usplash_write ]; then
			usplash_write "TEXT-URGENT $@"
		else
			echo "$@" >&2
		fi
	fi
	return 0
}

[ $DEBUG -eq $TRUE ] && msg "Executing cryptosmartcard.sh ..."
# flag tracking key-file availability
OPENED=$FALSE

# Terminate the PC/SC subsystem
killall -9 pcscd > /dev/null 2>&1 || true

# Is the USB driver loaded?
cat /proc/modules | busybox grep usb_storage >/dev/null 2>&1
USBLOAD=0$?
if [ $USBLOAD -gt 0 ]; then
	[ $DEBUG -eq $TRUE ] && msg "Loading driver 'usb_storage'"
	modprobe usb_storage >/dev/null 2>&1
fi

# Make sure USB device nodes have loaded
udevadm settle

# Relaunch the PC/SC subsystem
pcscd > /dev/null 2>&1

CARD_INSERTED=$FALSE
OPENED=$FALSE
LUKS_KEY_DIR=/tde_luks_keys
LUKS_KEY_COUNT=$(ls -1 $LUKS_KEY_DIR/ 2> /dev/null | wc -l)

DISK_UUID=$(blkid -s UUID -o value $CRYPTTAB_SOURCE)

if [ $LUKS_KEY_COUNT -gt 0 ]; then
	if [ "$DISK_UUID" != "" ]; then
		# Disk UUID found and LUKS keys are present
		# Check for card presence...
		pkcs15-tool --list-certificates > /dev/null 2>&1
		RET=$?
		if [ $RET -eq 0 ]; then
			# Card present
			CARD_INSERTED=$TRUE
			msg "SmartCard inserted, attempting to authenticate"

			if [ -e /dev/kmsg ]; then
				# Linux specific!
				# Wait for nonblocking random driver to start.
				# Without waiting, the pincheck utility can stall
				# for a very long time (forever?) waiting
				# for enough random data to start PKCS11.
				dmesg | grep -q "random: nonblocking pool is initialized" &> /dev/null
				RET=$?
				if [ $RET -ne 0 ]; then
					dmesg | grep -q "random: fast init done" &> /dev/null
					RET=$?
				fi
				LOOPS=0
				if [ $RET -ne 0 ]; then
					msg "Waiting for nonblocking random pool to start..."
					sleep 1
					while [ $RET -ne 0 ]; do
						dmesg | grep -q "random: nonblocking pool is initialized" &> /dev/null
						RET=$?
						if [ $RET -ne 0 ]; then
							sleep 1
							LOOPS=$((LOOPS+1))
							if [ $LOOPS -eq 10 ]; then
								msg "Random pool initialization is slow.  Try pressing keys or moving the mouse to speed it up..."
							fi
						fi
					done
					msg "Nonblocking pool started, continuing!"
				fi
				rm -f /tmp/kmsg
			fi

			PIN=$(cardpincheck $SYSTEM_PKCS_LIBRARY)
			RET=$?
			if [ $RET -eq 0 ]; then
				# PIN valid
				msg "SmartCard unlocked"
				for KEYFILE in ${LUKS_KEY_DIR}/${DISK_UUID}_slot*; do
					# Try decrypting
					echo "$PIN" | cardpincheck $SYSTEM_PKCS_LIBRARY $KEYFILE 2> /dev/null
					RET=$?
					if [ $RET -eq 0 ]; then
						OPENED=$TRUE
						break
					fi
				done
			else
				msg "SmartCard authentication failed"
			fi
		fi
	fi
fi

killall -9 pcscd > /dev/null 2>&1 || true

if [ $OPENED -eq $FALSE ]; then
	if [ $CARD_INSERTED -eq $TRUE ]; then
		msg "SmartCard LUKS keyfile invalid or incorrect SmartCard inserted"
		exit 0
	else
		msg "No SmartCard inserted or no LUKS keyfiles available on this system"
	fi
	if [ $HAS_PLYMOUTH -eq 1 ]; then
		plymouth ask-for-password --prompt="Please enter the LUKS password"
	else
		msg "Please enter the LUKS password: "
		read -s -r A < /dev/console
		echo -n "$A"
		msg "Attempting to authenticate..."
	fi
fi

exit 0
