#!/bin/sh
#
# usage: $0 description keytype keyfile [keybits]
#
# This handles sensitive data that must not be swapped out
# or written out to disk unencrypted.
#
# Important: before running this script the caller has
# to check for unsafe swap partitions. This is done in
# choose_method/encrypt/do_options.
#
# Assumption: This runs as part of d-i. Therefore, temp
# files outside of /target reside in a ramdisk. Process
# information (think [ "$pass" ]) is not exposed to anyone
# but the installing user.
#
# Released under the GNU GPL.
# Copyright 2005, Max Vozeler <xam@debian.org>
#

. /usr/share/debconf/confmodule

umask 077

# When changing minlen care must be taken about the
# consequences for translations, see #326482
minlen=8

passphrase_is_weak () {
	test $(printf %s "$1" | wc -c) -lt $minlen
}

get_passphrase () {
	local pass_ok

	pass_ok=0
	while [ $pass_ok -eq 0 ]; do
		templ="partman-crypto/passphrase"
		db_subst $templ DEVICE "$description"
		db_input critical $templ

		templ="partman-crypto/passphrase-again"
		db_input critical $templ

		db_go || return 1

		db_get partman-crypto/passphrase || RET=''
		pass=$RET
		if [ -z "$pass" ]; then
			db_set partman-crypto/passphrase ""
			db_set partman-crypto/passphrase-again ""
			templ="partman-crypto/passphrase-empty"
			db_fset $templ seen false
			db_input critical $templ
			db_fset partman-crypto/passphrase seen false
			db_fset partman-crypto/passphrase-again seen false
			continue
		fi

		db_get partman-crypto/passphrase-again || RET=''
		if [ "$pass" != "$RET" ]; then
			db_set partman-crypto/passphrase ""
			db_set partman-crypto/passphrase-again ""
			templ="partman-crypto/passphrase-mismatch"
			db_fset $templ seen false
			db_input critical $templ
			db_fset partman-crypto/passphrase seen false
			db_fset partman-crypto/passphrase-again seen false
			continue
		fi

		if passphrase_is_weak "$pass"; then
			templ="partman-crypto/weak_passphrase"
			db_subst $templ MINIMUM $minlen
			db_input critical $templ || true
			db_go || true
			db_get $templ || RET=''

			if [ "$RET" != true ]; then
				# user doesn't want to force weak passphrase
				db_set partman-crypto/passphrase ""
				db_set partman-crypto/passphrase-again ""
				db_fset partman-crypto/passphrase seen false
				db_fset partman-crypto/passphrase-again seen false
				continue
			fi

			db_set $templ false
			db_fset $templ seen false
		fi

		pass_ok=1
	done

	db_set partman-crypto/passphrase ""
	db_set partman-crypto/passphrase-again ""

	if [ $pass_ok -eq 1 ]; then
		echo "$pass"
	fi
}

have_entropy_plugin () {
	db_capb
	set -- $RET
	for cap; do
		if [ "$cap" = plugin-entropy ]; then
			return 0
		fi
	done
	return 1
}

# Fifo provided by cdebconf-entropy plugins
randfifo=/var/run/random.fifo

call_entropy_plugin () {
	local keybytes
	keybytes=$1

	db_capb backup align

	templ=partman-crypto/entropy
	db_fset $templ seen false
	db_subst $templ DEVICE "$description"
	db_subst $templ FIFO $randfifo
	db_subst $templ KEYSIZE "$keybytes"
	db_subst $templ SUCCESS partman-crypto/entropy-success
	db_input critical $templ
	db_go || return 1

	return 0
}

# Create a one-time random keyfile for use during install
create_random_keyfile() {
	local keyfile keybytes
	keyfile=$1
	keybytes=$2

	# Fork off file writer
	(while [ ! -p $randfifo ]; do
		 sleep 1
	 done
	 cat $randfifo > $keyfile
	) &
	writer_pid=$!

	# Call plugin to feed randfifo
	call_entropy_plugin $keybytes
	if [ $? -ne 0 ] || ! wait $writer_pid; then
		rm $keyfile
		kill $writer_pid
		return 1
	fi

	return 0
}

problem_dialog () {
	db_fset partman-crypto/keyfile-problem seen false
	db_input critical partman-crypto/keyfile-problem
	db_go || true
}

description=$1
keytype=$2
keyfile=$3
keybits=$4

if ! ([ "$description" ] && [ "$keytype" ] && [ "$keyfile" ]); then
	# bad options
	problem_dialog
	exit 1
fi

# Log available entropy
logger -t partman-crypto "kernel entropy_avail: $(cat /proc/sys/kernel/random/entropy_avail) bits"

if [ "$keytype" = random ]; then
	if ! have_entropy_plugin; then
		db_fset partman-crypto/tools_missing seen false
		db_input critical partman-crypto/tools_missing
		db_go || true
		exit 1
	fi
fi

case $keytype in
	passphrase)
		pass=$(get_passphrase)
		if [ -z "$pass" ]; then
			problem_dialog
			exit 1
		fi
		printf %s "$pass" > $keyfile
		;;

	random)
		if [ -z "$keybits" ]; then
			problem_dialog
			exit 1
		fi
		# Round keybits up to closest byte
		keybytes=$(( (keybits + 7)/8 ))
		if [ $keybytes -lt 1 ]; then
			# key size invalid
			problem_dialog
			exit 1
		fi
		if ! create_random_keyfile $keyfile $keybytes; then
			problem_dialog
			exit 1
		fi
		;;

	*)
		problem_dialog
		exit 1
		;;
esac
