[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] Enable partman-crypto to work with keys on removable devices



Hi all,

after a long hiatus I decided to do some d-i hacking again.

The attached path allows setting up a crypto device with a keyfile stored on another partition (mainly useful when that partition is on a usbkey).

In the "setup encrypted volumes" stage of partman, the user will be given a list of partitions known to partman and after selecting one, a path must be entered. If that file already exists on the device, it will be used as the keyfile, otherwise a new keyfile will be created.

I've done a test install using qemu (with a secondary qemu harddisk as the removable device) and a SVN version of cryptsetup (which has the necessary "mountdev" keyscript). Also, due to a bug in klibc, only ext3 is supported for now (bug reported, will be fixed before the next upload of cryptsetup which will allow any common fs to be used).

My d-i knowledge is rusty so a review of the patch would be much appreciated. (I've also been out of the loop wrt. d-i development, deadlines for the next release, etc...so I have no idea how suitable this patch is right now in the bigger picture)

I'm also planning to use some of the infrastructure of the patch to add support for two-factor keys (ask a passphrase, hash it, get a keyfile from usb stick, xor the two together, use that as the key) and smartcards (I've already ordered the hardware, dunno when I'll get it).

Cheers :)

--
David Härdeman
Index: debian/partman-crypto.templates
===================================================================
--- debian/partman-crypto.templates	(revision 53290)
+++ debian/partman-crypto.templates	(working copy)
@@ -269,8 +269,8 @@
 Type: text
 # :sl3:
 # This is a key type for encrypted file systems
-# It can be either protected by a passphrase, a keyfile
-# of a random key
+# It can be either protected by a passphrase, a keyfile,
+# a random key or a file stored on a removable device
 # This text is one of these choices, so keep it short
 _Description: Passphrase
 
@@ -278,8 +278,8 @@
 Type: text
 # :sl3:
 # This is a key type for encrypted file systems
-# It can be either protected by a passphrase, a keyfile
-# of a random key
+# It can be either protected by a passphrase, a keyfile,
+# a random key or a file stored on a removable device
 # This text is one of these choices, so keep it short
 _Description: Keyfile (GnuPG)
 
@@ -287,11 +287,20 @@
 Type: text
 # :sl3:
 # This is a key type for encrypted file systems
-# It can be either protected by a passphrase, a keyfile
-# of a random key
+# It can be either protected by a passphrase, a keyfile,
+# a random key or a file stored on a removable device
 # This text is one of these choices, so keep it short
 _Description: Random key
 
+Template: partman-crypto/text/keytype/removabledev
+Type: text
+# :sl3:
+# This is a key type for encrypted file systems
+# It can be either protected by a passphrase, a keyfile,
+# a random key or a file stored on a removable device
+# This text is one of these choices, so keep it short
+_Description: File on a removable device
+
 Template: partman-crypto/unsafe_swap
 Type: error
 # :sl3:
@@ -369,6 +378,37 @@
 _Description: Keyfile creation failure
  An error occurred while creating the keyfile.
 
+Template: partman-crypto/removable-source-partition
+Type: select
+Choices: ${CHOICES}
+# :sl3:
+_Description: Crypto key partition:
+ Please choose the partition which contains the key you wish to use or where
+ a key should be created.
+
+Template: partman-crypto/removable-source-path
+Type: string
+# :sl3:
+_Description: Path to crypto key:
+ Please enter the path (relative to the root of ${DEVICE}) where the key you
+ wish to use is stored or where a key should be created.
+
+Template: partman-crypto/removable-confirm-create
+Type: boolean
+Default: false
+# :sl3:
+_Description: Create new key?
+ No key was found on ${DEVICE} at path ${PATH}, do you wish to create
+ a new key?
+
+Template: partman-crypto/removable-bad-keyfile
+Type: error
+# :sl3:
+_Description: Invalid encryption key
+ You have selected a pre-existing key file which is not suitable as a
+ crypto key as it is not large enough. Please try using a different
+ key file.
+
 Template: partman-crypto/crypto_root_needs_boot
 Type: error
 # :sl3:
Index: ciphers/dm-crypt/keytype
===================================================================
--- ciphers/dm-crypt/keytype	(revision 53290)
+++ ciphers/dm-crypt/keytype	(working copy)
@@ -1 +1 @@
-passphrase random
+passphrase random removabledev
Index: finish.d/crypto_config
===================================================================
--- finish.d/crypto_config	(revision 53290)
+++ finish.d/crypto_config	(working copy)
@@ -96,6 +96,27 @@
 		keyfile="/dev/urandom"
 	elif [ $keytype = passphrase ]; then
 		keyfile="none"
+	elif [ $keytype = removabledev ]; then
+		local keydev keypath udevlinks tmp
+		keypath=$(cat $realdevdir/keypath)
+		keydev=$(cat $realdevdir/keydev)
+
+		# We need to use stable device names as using e.g. /dev/hda2
+		# will break the boot if a second USB key is present.
+		udevlinks=""
+		for tmp in by-id by-uuid by-label by-path; do
+			if [ -d "/dev/disk/$tmp" ]; then
+				udevlinks="$udevlinks /dev/disk/$tmp/*"
+			fi
+		done
+		for tmp in $udevlinks; do
+			if [ "$(readlink -f "$tmp")" = "$keydev" ]; then
+				keydev="$tmp"
+				break;
+			fi
+		done
+		keyfile="$keydev:$keypath"
+		opts="$opts,keyscript=mountdev"
 	elif [ -f $realdevdir/keyfile ]; then
 		keyfile=$(cat $realdevdir/keyfile)
 	else
Index: lib/crypto-base.sh
===================================================================
--- lib/crypto-base.sh	(revision 53290)
+++ lib/crypto-base.sh	(working copy)
@@ -219,6 +219,8 @@
 			setup_luks $cryptdev $realdev $cipher $ivalgorithm $keysize $keyfile || return 1
 		elif [ $keytype = random ]; then
 			setup_dmcrypt $cryptdev $realdev $cipher $ivalgorithm plain $keysize /dev/urandom || return 1
+		elif [ $keytype = removabledev ]; then
+			setup_dmcrypt $cryptdev $realdev $cipher $ivalgorithm plain $keysize $keyfile || return 1
 		else
 			setup_dmcrypt $cryptdev $realdev $cipher $ivalgorithm $keyhash $keysize $keyfile || return 1
 		fi
@@ -548,6 +550,8 @@
 
 	[ -d $part ] || return 1
 
+	rm -f $part/keydev
+	rm -f $part/keypath
 	case $type in
 	    dm-crypt)
 		echo aes > $part/cipher
@@ -795,8 +799,12 @@
 			type=$(cat $id/crypto_type)
 			keytype=$(cat $id/keytype)
 			cipher=$(cat $id/cipher)
+			keysize=""
+			[ -f $id/keysize ] && keysize=$(cat $id/keysize)
 
-			if [ $keytype = keyfile ] || [ $keytype = passphrase ]; then
+			if [ $keytype = keyfile      ] || \
+			   [ $keytype = passphrase   ] || \
+			   [ $keytype = removabledev ]; then
 				keyfile=$(mapdevfs $path | tr / _)
 				keyfile="$dev/$id/${keyfile#_dev_}"
 				if [ $type = loop-AES ]; then
@@ -804,7 +812,7 @@
 				fi
 
 				if [ ! -f $keyfile ]; then
-					if ! /bin/blockdev-keygen "$(humandev $path)" "$keytype" "$keyfile"; then
+					if ! /bin/blockdev-keygen "$(humandev $path)" "$keytype" "$keyfile" "$keysize"; then
 						db_fset partman-crypto/commit_failed seen false
 						db_input critical partman-crypto/commit_failed
 						db_go || true
Index: blockdev-keygen
===================================================================
--- blockdev-keygen	(revision 53290)
+++ blockdev-keygen	(working copy)
@@ -192,6 +192,110 @@
 	return 0
 }
 
+# Create or load an already created keyfile on a user-specified device
+create_removable_keyfile() {
+	local keyfile keybytes noninteractive source_dev source_id path mountpoint
+	keyfile=$1
+	keybytes=$2
+	noninteractive=true
+
+	. /lib/partman/lib/base.sh
+
+	while true; do
+		source_dev=''
+		source_id=''
+		while [ ! "$source_id" ]; do
+			choices=$(partition_tree_choices)
+			debconf_select critical partman-crypto/removable-source-partition "$choices" blah
+
+			case $? in
+			1)
+				$noninteractive
+				;;
+			255)
+				return 1
+				;;
+			esac
+			noninteractive='return 1'
+			source_dev=${RET%//*}
+			source_id=${RET#*//}
+		done
+		source_dev=${source_dev##*/}
+
+		cd "$DEVICES/$source_dev" || return 1
+
+		local x1 x2 x3 x4 x5 device x6
+		open_dialog PARTITION_INFO "$source_id"
+		read_line x1 x2 x3 x4 x5 device x6
+		close_dialog
+
+		if [ -z "$device" ]; then
+			return 1
+		fi
+
+		defpath="/keys/$(cat /etc/hostname)$(echo "$device" | sed 's/\//_/g')"
+		db_set partman-crypto/removable-source-path "$defpath"
+		db_subst partman-crypto/removable-source-path DEVICE "$(humandev $device)"
+		db_input critical partman-crypto/removable-source-path || true
+		db_go || return 1
+		db_get partman-crypto/removable-source-path
+		path="$RET"
+
+		if [ -z "$path" ]; then
+			continue;
+		fi
+
+		mountpoint="/tmp/blockdev-keygen-tmpmount"
+		if [ ! -e "/tmp/blockdev-keygen-tmpmount" ]; then
+			mkdir "$mountpoint" || return 1
+		fi
+
+		if ! log-output -t blockdev-keygen \
+		     mount "$device" "$mountpoint" > /dev/null 2>&1; then
+			return 1
+		fi
+
+		local target filesize
+		target="${mountpoint}/${path}"
+		if [ -e "$target" ]; then
+			# Check that the keyfile is suitable
+			filesize="$(ls -l1 "$target" | sed s'/[[:space:]]\+/ /g' | cut -d' ' -f3)"
+			if [ "$filesize" -lt "$keybytes" ]; then
+				db_fset partman-crypto/removable-bad-keyfile seen false
+				db_input critical partman-crypto/removable-bad-keyfile
+				db_go || true
+				continue
+			fi
+		else
+			# We need to create a new keyfile
+			db_subst partman-crypto/removable-confirm-create DEVICE "$(humandev $device)"
+			db_subst partman-crypto/removable-confirm-create PATH "$path"
+			db_input critical partman-crypto/removable-confirm-create || true
+			db_go || return 1
+			db_get partman-crypto/removable-confirm-create
+			if [ "$RET" != true ]; then
+				umount "$mountpoint" > /dev/null 2>&1 || return 1
+				continue
+			fi
+			db_reset partman-crypto/removable-confirm-create
+
+			if ! mkdir -p "$(dirname "$target")" || \
+			   ! create_random_keyfile "$target" "$keybytes"; then
+				umount "$mountpoint" > /dev/null 2>&1 || return 1
+				return 1
+			fi
+		fi
+
+		cp "$target" "$keyfile"
+		echo "$device" > "$(dirname "$keyfile")/keydev"
+		echo "$path" > "$(dirname "$keyfile")/keypath"
+		echo "plain" > "$(dirname "$keyfile")/keyhash"
+		umount "$mountpoint" > /dev/null 2>&1 || return 1
+		break
+	done
+	return 0
+}
+
 problem_dialog () {
 	db_fset partman-crypto/keyfile-problem seen false
 	db_input critical partman-crypto/keyfile-problem
@@ -212,7 +316,7 @@
 # Log available entropy
 logger -t partman-crypto "kernel entropy_avail: $(cat /proc/sys/kernel/random/entropy_avail) bits"
 
-if [ "$keytype" = random ] || [ "$keytype" = keyfile ]; then
+if [ "$keytype" = random ] || [ "$keytype" = keyfile ] || [ "$keytype" = removabledev ]; then
 	if ! have_entropy_plugin; then
 		db_fset partman-crypto/tools_missing seen false
 		db_input critical partman-crypto/tools_missing
@@ -261,6 +365,24 @@
 		fi
 		;;
 
+	removabledev)
+		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_removable_keyfile $keyfile $keybytes; then
+			problem_dialog
+			exit 1
+		fi
+		;;
+
 	*)
 		problem_dialog
 		exit 1

Reply to: