#!/bin/sh

# initramfs magic

PREREQ=""
prereqs()
{
	echo "$PREREQ"
}

case $1 in
	prereqs)
		prereqs
		exit 0
		;;
esac

. /scripts/functions

# Begin real processing below this line

CRYPTLOG="/run/initramfs/opencrypt.log"
TMPCRYPTLOG="${CRYPTLOG}.tmp"
CONFFILE="/etc/opencrypt/opencrypt.conf"
LOCALPREFIX="localfile$$"

# we want to use our binary programs
PATH="/usr/local/bin:$PATH"
export PATH

. /usr/share/opencrypt/functions.sh
. /usr/share/opencrypt/script-functions.sh

try_open() {
	local slot checksum
	cryptlog "try open $MAP with $MODE"
	if [ "$VERBOSE" = "yes" ]; then
		cryptlog "options: $@"
	fi
	if [ "$MODE" = "keyboard" ]; then
		/sbin/cryptsetup open "$@" --debug "$CRYPT" "$MAP" >> "$TMPCRYPTLOG"
	else
		/sbin/cryptsetup open "$@" --debug "$CRYPT" "$MAP" >> "$TMPCRYPTLOG" 2>> "$CRYPTLOG"
	fi
	if [ "$?" = "0" ]; then
		cryptlog "$MAP successfully opened with $MODE"
		OPEN="true"
	else
		cryptlog "open $MAP failed with parameters: $@ open $CRYPT $MAP"
	fi
	slot=$(grep unlocked "$TMPCRYPTLOG")
	copy_tmp_log
	if [ "x$slot" != "x" ]; then
		cryptlog "$slot"
	else
		cryptlog "No key slot unlocked."
	fi
	if [ -s "$HASH" ]; then
		checksum=$(sha256sum "$HASH" 2>&1 | cut -d' ' -f1)
		cryptlog "Used key checksum $checksum"
	fi
}

setup_firewall() {
	cryptlog "setup firewall"

	# setup a minimal firewall with almost all dropped
	# accept igmp, icmp and established/related
	if [ "$FIREWALL" = "iptables" -a -f "/etc/opencrypt/iptables.conf" ]; then
		cryptlog "configuring iptables"
		/usr/sbin/iptables-restore "/etc/opencrypt/iptables.conf"
	fi
	if [ "$FIREWALL" = "iptables" -a -f "/etc/opencrypt/ip6tables.conf" ]; then
		cryptlog "configuring ip6tables"
		/usr/sbin/ip6tables-restore "/etc/opencrypt/ip6tables.conf"
	fi
	if [ "$FIREWALL" = "nftables" -a -f "/etc/opencrypt/nftables.conf" ]; then
		cryptlog "configuring nftables"
		/usr/sbin/nft -f "/etc/opencrypt/nftables.conf"
	fi
}

check_device() {
	local device checkdev
	device=$1
	cryptlog "checking $device"
	checkdev=$(/usr/local/bin/ifquery --state $device 2>&1)
	if [ "$checkdev" != "$device=$device" ]; then
		cryptlog "configuring $device using ifup"
		/usr/local/bin/ifup $device >> "$TMPCRYPTLOG" 2>&1
		copy_tmp_log
	fi
}

open_network() {
	local devices device
	cryptlog "opening network"

	[ "$NETWORK" = "up" ] && return

	if [ -f "/etc/network/interfaces" ]; then
		# busybox ifup fumble on some regular stances
		# so we use real ifup
		devices=$(/usr/local/bin/ifquery --list --all 2>&1)
		for device in $devices ; do
			check_device $device
		done
	else
		# configure_networking will use kernel parameter ip
		cryptlog "configuring networking with kernel parameters"
		configure_networking >> "$TMPCRYPTLOG" 2>&1
		copy_tmp_log
	fi

	# we check state set to UP, i.e. when a connection is actually established
	# ifquery checks only if the device is configured
	# and busybox ip doesn't show this state, so we need our binary
	if [ "x$WAITFOR" != "x" ]; then
		checklink=$(/usr/local/bin/ip link show $WAITFOR 2>&1 | grep 'state UP') && NETWORK="up"
		[ "$NETWORK" != "up" ] && sleep 2
	else
		checklink=$(/usr/local/bin/ip link show 2>&1 | grep 'state UP') && NETWORK="up"
		[ "$NETWORK" != "up" ] && sleep 2
	fi
}

device_exist() {
	local device=$1
	device=$(echo $device | sed 's/^.*=//')
	found=$(blkid | grep $device)
	[ "x$found" != "x" ] && return 0
	return 1
}

open_map() {
	local status options
	cryptlog "opening $MAP $CRYPT"
	status=$(cryptsetup status "$MAP" | grep 'is active')
	if [ "x$status" != "x" ]; then
		cryptlog "$MAP already open, skipping"
		return
	fi
	if ! device_exist $CRYPT ; then
		cryptlog "$CRYPT not found, skipping"
		return
	fi

	OPEN="false"
	while [ "$OPEN" = "false" ]; do
		scan_devices
		create_hash
		if [ ! -s "$HASH" ]; then
			scan_network
			create_hash
		fi
		if [ -s "$HASH" ]; then
			options="--type=$CRYPTYPE --key-file=$HASH"
			MODE="remote key"
			if [ "$CRYPTYPE" = "luks" -a "x$REMOTEKEYSLOT" != "x" ]; then
				options="$options --key-slot=$REMOTEKEYSLOT"
			fi
			try_open $options
		fi
		if [ "$OPEN" = "false" ]; then
			options="--type=$CRYPTYPE"
			MODE="keyboard"
			if [ "$CRYPTYPE" = "luks" -a "x$KEYBOARDKEYSLOT" != "x" ]; then
				options="$options --key-slot=$KEYBOARDKEYSLOT"
			fi
			if [ "$CRYPTYPE" != "loopaes" -a "x$KEYBOARDTIMEOUT" != "x" ]; then
				options="$options --timeout=$KEYBOARDTIMEOUT"
			fi
			try_open $options
		fi
	done
}

cryptlog "starting opencrypt"

# /cryptroot/crypttab could be empty but must exists
if [ ! -f "/cryptroot/crypttab" ]; then
	cryptlog "no crypttab found"
	exit 1
fi

load_params

NETWORK="down"
HASH="hash$$"

# cannot do mount in /run/initramfs so stay in current dir
TMP="tmp$$"
TMP2="tmp2$$"
mkdir -p "$TMP" "$TMP2"

# we need 2 steps because cryptsetup fails if we are reading a file

# first we use our custom crypttab to ease device name changes
# in which case /cryptroot/crypttab will be empty
# as initramfs uses the mounted root when creating initrd
CRYPTYPES="luks|tcrypt|bitlk|fvault2|plain"
cryptlog "loading custom crypttab"
while read MAP CRYPT KEY PARAM ; do
	MAP=$(echo "$MAP" | cut -d'#' -f1)
	if [ "x$MAP" != "x#" -a "x$MAP" != "x" ]; then
		cryptlog "preparing $MAP $CRYPT"
		check=$(echo "$CRYPT" | grep '^/')
		if [ "x$check" != "x" ]; then
			CRYPT=$(echo "$CRYPT" | sed 's/\//__/g')
		fi
		crypttype=$(echo "$PARAM" | sed -r "s/.*($CRYPTYPES).*/\1/" | grep -E "($CRYPTYPES)")
		if [ "x$crypttype" = "x" ]; then
			crypttype="luks"
		fi
		echo "$MAP $crypttype" > "$TMP/$CRYPT"
	fi
done < "/etc/opencrypt/crypttab"

# next we override mapping with system crypttab mapping
# which contains only filesystems needed for booting
# (usually only root)
cryptlog "loading system crypttab"
while read MAP CRYPT KEY PARAM ; do
	MAP=$(echo "$MAP" | cut -d'#' -f1)
	# initramfs should include only crypt needing a password but we recheck it
	if [ "$KEY" = "none" -a "x$MAP" != "x#" -a "x$MAP" != "x" ]; then
		cryptlog "preparing $MAP $CRYPT"
		check=$(echo "$CRYPT" | grep '^/')
		if [ "x$check" != "x" ]; then
			CRYPT=$(echo "$CRYPT" | sed 's/\//__/g')
		fi
		echo "$MAP" > "$TMP/$CRYPT"
		crypttype=$(echo "$PARAM" | sed -r "s/.*($CRYPTYPES).*/\1/" | grep -E "($CRYPTYPES)")
		if [ "x$crypttype" = "x" ]; then
			crypttype="luks"
		fi
		echo "$MAP $crypttype" > "$TMP/$CRYPT"
	fi
done < "/cryptroot/crypttab"

if [ "x$FIREWALL" != "x" ]; then
	setup_firewall
fi

if used_network_modes ; then
	try=0
	while [ $try -lt 15 -a "$NETWORK" = "down" ]; do
		try=$(expr $try + 1)
		if [ "$VERBOSE" = "yes" ]; then
			cryptlog "open network try $try"
		fi
		open_network
	done
	if [ "$NETWORK" = "up" ]; then
		cryptlog "network up"
	else
		cryptlog "network down"
	fi
	if [ "$VERBOSE" = "yes" ]; then
		/usr/local/bin/ip addr >> ${TMPCRYPTLOG} 2>&1
		copy_tmp_log
		/usr/local/bin/ip route >> ${TMPCRYPTLOG} 2>&1
		copy_tmp_log
	fi
fi

# now we can try to search the keys and open the partitions
for device in ${TMP}/* ; do
	CRYPT=$(echo "$device" | cut -d'/' -f2)
	check=$(echo "$CRYPT" | grep '^_')
	if [ "x$check" != "x" ]; then
		CRYPT=$(echo "$CRYPT" | sed 's/__/\//g')
	fi
	MAP=$(cat "$device" | cut -d' ' -f1)
	CRYPTYPE=$(cat "$device" | cut -d' ' -f2)
	open_map
done

# do not close network as it is correctly configured
# and other networking tools could be disabled

umount_devices

cryptlog "ending opencrypt"
exit 0

