#529343 debian-installer: physical volume for encryption: doesnt care if already encrypted and kills data

#529343#5
Date:
2009-05-18 19:17:26 UTC
From:
To:
When having to reinstall a system with an encrypted root-partition, the installer gives no means to
reuse a physical colume for encryption which has already been set up in a previous install.

The user isn't warned and the data contained in the encrypted volume will be killed without notice,
even if the same password is supplied.

I suggest to give the user the option of reusing the encrypted device or at least give a big fat warning
that his data will be killed, if he proceeds one step further.

#529343#10
Date:
2009-05-19 05:02:42 UTC
From:
To:
reassign 529343 partman-crypto
forcemerge 451535 529343
thanks

Quoting Tobias Winter (tobias@linuxdingsda.de):


Already reported as #451535. given the current lack of manpower
working on partman-crypto, I doubt this is fixed unless someone comes
up with a patch.

Please note that #451535 points to
http://wiki.debian.org/DebianInstaller/Rescue/Crypto that documents a
method to reuse existing encrypted filesystems. Please also note that
I have absolutely no clue about all this..:)

#529343#21
Date:
2011-09-07 14:59:16 UTC
From:
To:
Here's a first pass at this.  What do people think?

The one thing I don't think I've got right yet is writing out
/etc/crypttab at the end of installation.  This needs a bit more work to
write out the correct files in the partman device directory without
causing partman to reinitialise the encrypted volume.

  * Add an "Activate existing encrypted volumes" option to the
    partman-crypto main menu.  If selected, this searches for existing
    volumes, and for each one prompts for its passphrase and attempts to
    open it; it then returns directly to the partitioning menu
    (closes: #529343, LP: #420080).

=== modified file 'choose_partition/crypto/do_option'
--- choose_partition/crypto/do_option	2009-11-10 14:20:25 +0000
+++ choose_partition/crypto/do_option	2011-09-07 14:18:17 +0000
@@ -12,6 +12,113 @@

 . /lib/partman/lib/crypto-base.sh

+find_encrypted_partitions () {
+	local ret dev num id size type fs path name
+
+	ret=1
+	for dev in $DEVICES/*; do
+		[ -d "$dev" ] || continue
+		cd "$dev"
+
+		open_dialog PARTITIONS
+		while { read_line num id size type fs path name; [ "$id" ]; }; do
+			[ "$ret" = 1 ] || continue
+			[ "$fs" != free ] || continue
+			if cryptsetup isLuks "$path" 2>/dev/null; then
+				ret=0
+			fi
+		done
+		close_dialog
+
+		if [ "$ret" = 0 ]; then
+			return 0
+		fi
+	done
+
+	return 1
+}
+
+get_passphrase () {
+	db_set partman-crypto/passphrase-existing ""
+	db_fset partman-crypto/passphrase-existing seen false
+	db_subst partman-crypto/passphrase-existing DEVICE "$1"
+	db_input critical partman-crypto/passphrase-existing
+
+	db_go || return 1
+
+	db_get partman-crypto/passphrase-existing || RET=''
+	echo -n "$RET"
+}
+
+do_cryptsetup () {
+	local id path cryptdev pass
+
+	id="$1"
+	path="$2"
+	cipher="$(cryptsetup luksDump "$path" |
+		  sed -n '/^Cipher name:/s/.*[[:space:]]//p')"
+	if [ "$cipher" ]; then
+		crypto_load_modules dm-crypt "$cipher"
+	fi
+
+	cryptdev="${path##*/}_crypt"
+	if ! cryptsetup status "$cryptdev" >/dev/null 2>&1; then
+		while :; do
+			pass="$(get_passphrase "$path")" || return 1
+			if [ -z "$pass" ]; then
+				return 1
+			fi
+			echo -n "$pass" | log-output -t partman-crypto \
+				cryptsetup -d - luksOpen "$path" "$cryptdev" \
+				&& break
+		done
+
+		echo "$cryptdev" >"$id/crypt_active"
+		db_subst partman-crypto/text/in_use DEV "${cryptdev##*/}"
+		db_metaget partman-crypto/text/in_use description
+		partman_lock_unit "$(mapdevfs "$path")" "$RET"
+	fi
+}
+
+do_activate () {
+	local dev partitions num id size type fs path name part
+
+	for dev in $DEVICES/*; do
+		[ -d "$dev" ] || continue
+		cd "$dev"
+
+		partitions=
+		open_dialog PARTITIONS
+		while { read_line num id size type fs path name; [ "$id" ]; }; do
+			[ "$fs" != free ] || continue
+			partitions="$partitions $id,$path"
+		done
+		close_dialog
+
+		for part in $partitions; do
+			id="${part%%,*}"
+			path="${part#*,}"
+
+			if cryptsetup isLuks "$path" 2>/dev/null; then
+				do_cryptsetup "$id" "$path" || continue
+			fi
+		done
+	done
+
+	# Encrypted devices as configured by d-i usually contain LVM PVs
+	export LVM_SUPPRESS_FD_WARNINGS=1
+	log-output -t partman-crypto pvscan
+	log-output -t partman-crypto vgscan
+	log-output -t partman-crypto vgchange -a y
+
+	# Tell partman to detect filesystems again.
+	rm -f /var/lib/partman/filesystems_detected
+
+	stop_parted_server
+	restart_partman
+	exit 0
+}
+
 do_create () {
 	local parts line pv output vg pathmap
 	parts=""
@@ -89,10 +196,25 @@ confirm_changes partman-crypto || exit 0
 commit_changes partman-crypto/commit_failed || exit $?

 while :; do
+	CHOICES=
+	DESCRIPTIONS=
+	add_choice () {
+		CHOICES="${CHOICES:+$CHOICES, }$1"
+		db_metaget "partman-crypto/mainmenu/$1" description
+		DESCRIPTIONS="${DESCRIPTIONS:+$DESCRIPTIONS, }$RET"
+	}
+	if find_encrypted_partitions; then
+		add_choice activate
+	fi
+	add_choice create
+	add_choice finish
+	db_subst partman-crypto/mainmenu CHOICES "$CHOICES"
+	db_subst partman-crypto/mainmenu DESCRIPTIONS "$DESCRIPTIONS"
 	db_input critical partman-crypto/mainmenu
 	db_go || exit 10
 	db_get partman-crypto/mainmenu
 	case $RET in
+	    activate)	do_activate ;; # does not return
 	    create)	do_create ;;
 	    finish)	break ;;
 	    *)

=== modified file 'debian/partman-crypto.templates'
--- debian/partman-crypto.templates	2009-12-05 22:29:36 +0000
+++ debian/partman-crypto.templates	2011-09-06 23:21:59 +0000
@@ -364,6 +364,14 @@ _Description: Use weak passphrase?
  You entered a passphrase that consists of less than ${MINIMUM} characters,
  which is considered too weak. You should choose a stronger passphrase.

+Template: partman-crypto/passphrase-existing
+Type: password
+# :sl3:
+_Description: Passphrase for ${DEVICE}:
+ Please enter the passphrase for the encrypted volume ${DEVICE}.
+ .
+ If you don't enter anything, the volume will not be activated.
+
 Template: partman-crypto/entropy
 Type: entropy
 # :sl3:
@@ -430,15 +438,35 @@ _Description: Proceed to install crypto

 Template: partman-crypto/mainmenu
 Type: select
-Choices-C: create, finish
+Choices-C: ${CHOICES}
+Choices: ${DESCRIPTIONS}
+# :sl3:
+_Description: Encryption configuration actions
+ This menu allows you to configure encrypted volumes.
+
+Template: partman-crypto/mainmenu/activate
+Type: text
 # Note to translators : Please keep your translations of the choices
 # below a 65 columns limit (which means 65 characters
 # in single-byte languages)
 # :sl3:
-__Choices: Create encrypted volumes, Finish
+_Description: Activate existing encrypted volumes
+
+Template: partman-crypto/mainmenu/create
+Type: text
+# Note to translators : Please keep your translations of the choices
+# below a 65 columns limit (which means 65 characters
+# in single-byte languages)
 # :sl3:
-_Description: Encryption configuration actions
- This menu allows you to configure encrypted volumes.
+_Description: Create encrypted volumes
+
+Template: partman-crypto/mainmenu/finish
+Type: text
+# Note to translators : Please keep your translations of the choices
+# below a 65 columns limit (which means 65 characters
+# in single-byte languages)
+# :sl3:
+_Description: Finish

 Template: partman-crypto/create/partitions
 Type: multiselect

#529343#26
Date:
2011-09-09 13:13:36 UTC
From:
To:
I meant to send my previous version to the first of the merged bug set,
#451535.  I'll send further mails only there rather than to #529343 as
well.

Well.  Yes.  That turned out to be the second 90% of the work!  After
trying a few alternatives, I ended up with a new 'crypto_keep' method
and then tried to let init.d/crypto do as much of the work as possible,
while still being careful to avoid reinitialising the contents of
encrypted volumes.

In the process, I also decided that it was better to always have the
Activate option present, without trying to detect existing volumes
first.  That way, we can actively warn people that this method only
works with LUKS where we have a useful encrypted volume header and that
they should back up their data before attempting an installation, rather
than having them get confused into destroying their data as before.

I'm fairly happy with this now, and am inclined to commit it if there
are no objections.  The one problem I've found is that the check for an
unencrypted /boot doesn't work properly when activating existing
LVM-on-crypto volumes, but I think that's actually a pre-existing bug so
I'm not going to let that block this change.

  * Add an "Activate existing encrypted volumes" option to the
    partman-crypto main menu.  If selected, this searches for existing
    volumes, and for each one prompts for its passphrase and attempts to
    open it; it then returns directly to the partitioning menu (closes:
    #451535, LP: #420080).

=== modified file 'check.d/crypto_check_mountpoints'
--- check.d/crypto_check_mountpoints	2008-03-14 19:25:59 +0000
+++ check.d/crypto_check_mountpoints	2011-09-08 19:20:22 +0000
@@ -43,7 +43,7 @@ for dev in $DEVICES/*; do
 		[ -f $realdevdir/method ] || continue
 		method=$(cat $realdevdir/method)
 		type=$(cat $realdevdir/crypto_type)
-		[ $method = crypto ] || continue
+		[ $method = crypto ] || [ $method = crypto_keep ] || continue

 		# Check 1 - Is cryptoroot possible?
 		if [ "$mnt" = / ]; then

=== modified file 'choose_partition/crypto/do_option'
--- choose_partition/crypto/do_option	2009-11-10 14:20:25 +0000
+++ choose_partition/crypto/do_option	2011-09-09 11:30:35 +0000
@@ -12,6 +12,118 @@

 . /lib/partman/lib/crypto-base.sh

+get_passphrase () {
+	db_set partman-crypto/activate/passphrase-existing ""
+	db_fset partman-crypto/activate/passphrase-existing seen false
+	db_subst partman-crypto/activate/passphrase-existing DEVICE "$1"
+	db_input critical partman-crypto/activate/passphrase-existing
+
+	db_go || return 1
+
+	db_get partman-crypto/activate/passphrase-existing || RET=''
+	echo -n "$RET"
+}
+
+do_cryptsetup () {
+	local dev num id size path
+	local dump cipher keysize ivalgorithm keytype keyhash
+	local cryptdev pass
+
+	dev=$1
+	num=$2
+	id=$3
+	size=$4
+	path=$5
+
+	dump="$(cryptsetup luksDump "$path")"
+	cipher="$(echo "$dump" | sed -n '/^Cipher name:/s/.*[[:space:]]//p')"
+	if [ "$cipher" ]; then
+		crypto_load_udebs "cdebconf-$DEBIAN_FRONTEND-entropy" \
+				  partman-crypto-dm
+		crypto_check_required_tools dm-crypt
+		crypto_load_modules dm-crypt "$cipher"
+	fi
+	keysize="$(echo "$dump" | sed -n '/^MK bits:/s/.*[[:space:]]//p')"
+	ivalgorithm="$(echo "$dump" | sed -n '/^Cipher mode:/s/.*[[:space:]]//p')"
+	keytype=passphrase
+	keyhash="$(echo "$dump" | sed -n '/^Hash spec:/s/.*[[:space:]]//p')"
+
+	cryptdev="${path##*/}_crypt"
+	if ! cryptsetup status "$cryptdev" >/dev/null 2>&1; then
+		while :; do
+			pass="$(get_passphrase "$path")" || return 1
+			if [ -z "$pass" ]; then
+				return 1
+			fi
+			echo -n "$pass" | log-output -t partman-crypto \
+				cryptsetup -d - luksOpen "$path" "$cryptdev" \
+				&& break
+		done
+
+		cryptdev="/dev/mapper/$cryptdev"
+		echo dm-crypt > $id/crypto_type
+		echo "$keysize" > $id/keysize
+		echo "$ivalgorithm" > $id/ivalgorithm
+		echo "$keytype" > $id/keytype
+		echo "$keyhash" > $id/keyhash
+		echo cipher > $id/cipher
+		echo crypto_keep > $id/method
+		echo "$cryptdev" > $id/crypt_active
+
+		db_subst partman-crypto/text/in_use DEV "${cryptdev##*/}"
+		db_metaget partman-crypto/text/in_use description
+		partman_lock_unit "$(mapdevfs "$path")" "$RET"
+	fi
+}
+
+do_activate () {
+	local found_luks dev partitions num id size type fs path name part
+
+	found_luks=0
+	for dev in $DEVICES/*; do
+		[ -d "$dev" ] || continue
+		cd "$dev"
+
+		partitions=
+		open_dialog PARTITIONS
+		while { read_line num id size type fs path name; [ "$id" ]; }; do
+			[ "$fs" != free ] || continue
+			partitions="$partitions $id,$path"
+		done
+		close_dialog
+
+		for part in $partitions; do
+			id="${part%%,*}"
+			path="${part#*,}"
+
+			if cryptsetup isLuks "$path" 2>/dev/null; then
+				found_luks=1
+				do_cryptsetup "$dev" "$num" "$id" "$size" \
+					"$path" || continue
+			fi
+		done
+	done
+
+	if [ "$found_luks" = 0 ]; then
+		db_input critical partman-crypto/activate/no_luks
+		db_go || true
+		return
+	fi
+
+	# Encrypted devices as configured by d-i usually contain LVM PVs
+	export LVM_SUPPRESS_FD_WARNINGS=1
+	log-output -t partman-crypto pvscan
+	log-output -t partman-crypto vgscan
+	log-output -t partman-crypto vgchange -a y
+
+	# Tell partman to detect filesystems again.
+	rm -f /var/lib/partman/filesystems_detected
+
+	stop_parted_server
+	restart_partman
+	exit 0
+}
+
 do_create () {
 	local parts line pv output vg pathmap
 	parts=""
@@ -93,6 +231,7 @@ while :; do
 	db_go || exit 10
 	db_get partman-crypto/mainmenu
 	case $RET in
+	    activate)	do_activate ;; # exits if any volumes were activated
 	    create)	do_create ;;
 	    finish)	break ;;
 	    *)

=== modified file 'debian/control'
--- debian/control	2011-05-03 16:05:09 +0000
+++ debian/control	2011-09-09 12:06:37 +0000
@@ -12,7 +12,7 @@ Vcs-Bzr: http://bazaar.launchpad.net/~ub
 Package: partman-crypto
 XC-Package-Type: udeb
 Architecture: any
-Depends: partman-base (>= 134), cdebconf-udeb (>= 0.133), di-utils (>= 1.68), ${shlibs:Depends}, ${misc:Depends}
+Depends: partman-base (>= 134), partman-lvm (>= 62), cdebconf-udeb (>= 0.133), di-utils (>= 1.68), ${shlibs:Depends}, ${misc:Depends}
 Description: Add to partman support for block device encryption

 Package: partman-crypto-dm

=== modified file 'debian/partman-crypto.templates'
--- debian/partman-crypto.templates	2009-12-05 22:29:36 +0000
+++ debian/partman-crypto.templates	2011-09-08 11:16:40 +0000
@@ -430,12 +430,12 @@ _Description: Proceed to install crypto

 Template: partman-crypto/mainmenu
 Type: select
-Choices-C: create, finish
+Choices-C: activate, create, finish
 # Note to translators : Please keep your translations of the choices
 # below a 65 columns limit (which means 65 characters
 # in single-byte languages)
 # :sl3:
-__Choices: Create encrypted volumes, Finish
+__Choices: Activate existing encrypted volumes, Create encrypted volumes, Finish
 # :sl3:
 _Description: Encryption configuration actions
  This menu allows you to configure encrypted volumes.
@@ -454,3 +454,20 @@ Type: error
 # :sl3:
 _Description: No devices selected
  No devices were selected for encryption.
+
+Template: partman-crypto/activate/no_luks
+Type: error
+# :sl3:
+_Description: No LUKS devices found
+ This partitioning program can only activate existing encrypted volumes that
+ use the LUKS format (dm-crypt with a passphrase). No such volumes were
+ found. If you have encrypted volumes using other formats, you may need to
+ back up your data before continuing with installation.
+
+Template: partman-crypto/activate/passphrase-existing
+Type: password
+# :sl3:
+_Description: Passphrase for ${DEVICE}:
+ Please enter the passphrase for the encrypted volume ${DEVICE}.
+ .
+ If you don't enter anything, the volume will not be activated.

=== modified file 'finish.d/crypto_aptinstall'
--- finish.d/crypto_aptinstall	2008-03-20 21:06:33 +0000
+++ finish.d/crypto_aptinstall	2011-09-07 22:17:00 +0000
@@ -39,7 +39,7 @@ for dev in $DEVICES/*; do
 		[ -f $id/crypto_type ] || continue

 		method=$(cat $id/method)
-		[ $method = crypto ] || continue
+		[ $method = crypto ] || [ $method = crypto_keep ] || continue

 		type=$(cat $id/crypto_type)
 		case $type in

=== modified file 'init.d/crypto'
--- init.d/crypto	2010-05-27 09:44:55 +0000
+++ init.d/crypto	2011-09-09 12:36:17 +0000
@@ -4,6 +4,17 @@
 # setup in choose_partition/crypto/do_option.

 . /lib/partman/lib/base.sh
+. /lib/partman/lib/lvm-base.sh
+
+# Avoid warnings from lvm2 tools about open file descriptors
+export LVM_SUPPRESS_FD_WARNINGS=1
+
+if [ -x /sbin/vgdisplay ]; then
+	vgroups=$(/sbin/vgdisplay 2>/dev/null | grep '^[ ]*VG Name' | \
+		sed -e 's/.*[[:space:]]\(.*\)$/\1/' | sort)
+else
+	vgroups=''
+fi

 dev_to_devdir () {
 	echo $DEVICES/$(echo $1 | tr / =)
@@ -72,7 +83,7 @@ create_partition () {
 }

 create_cryptdisk () {
-	local dev id num size path cryptdev cipher
+	local dev id num size path cryptdev cipher file vg vgs
 	dev=$1
 	id=$2
 	num=$3
@@ -81,6 +92,7 @@ create_cryptdisk () {

 	cipher=$(cat $id/cipher)
 	keytype=$(cat $id/keytype)
+	method=$(cat $id/method)

 	templ="partman-crypto/text/cryptdev_description"
 	db_subst $templ CIPHER $cipher
@@ -128,17 +140,47 @@ create_cryptdisk () {
 	case $filesystem in
 		linux-swap)
 			echo swap > $cryptpart/method
-			>$cryptpart/format
+			if [ "$method" = crypto ]; then
+				>$cryptpart/format
+			else
+				rm -f $cryptpart/format
+			fi
 			;;

 		$default_fs)
-			echo format > $cryptpart/method
-			>$cryptpart/format
-			>$cryptpart/use_filesystem
-			echo $filesystem > $cryptpart/filesystem
+			if [ "$method" = crypto ]; then
+				echo format > $cryptpart/method
+				>$cryptpart/format
+				>$cryptpart/use_filesystem
+				echo $filesystem > $cryptpart/filesystem
+			else
+				echo keep > $cryptpart/method
+				rm -f $cryptpart/format
+			fi
 			;;
 	esac

+	# To avoid ordering problems between init.d/crypto and init.d/lvm,
+	# we need to duplicate a bit of the latter here, in case an existing
+	# crypto device contains an LVM PV.
+	if [ "$method" = crypto_keep ]; then
+		if pvdisplay "$cryptdev" >/dev/null 2>&1; then
+			for file in acting_filesystem filesystem format \
+				    formatable use_filesystem; do
+				rm -f $cryptpart/$file
+			done
+			echo lvm > $cryptpart/method
+			if [ ! -e $cryptpart/locked ]; then
+				vg="$(pv_get_vg "$cryptdev")"
+				for vgs in $vgroups; do
+					if [ "$vg" = "$vgs" ]; then
+						vg_lock_pvs "$vg" "$cryptdev"
+					fi
+				done
+			fi
+		fi
+	fi
+
 	update_partition $cryptdir $cryptid

 	echo $path:$num:$dev/$id > $cryptdir/crypt_realdev
@@ -174,7 +216,7 @@ for dev in /var/lib/partman/devices/*; d
 		[ -f $id/crypt_active ] || continue

 		method=$(cat $id/method)
-		[ $method = crypto ] || continue
+		[ $method = crypto ] || [ $method = crypto_keep ] || continue

 		if ! create_cryptdisk $dev $id $num $size $path; then
 			db_fset partman-crypto/init_failed seen false

=== modified file 'lib/crypto-base.sh'
--- lib/crypto-base.sh	2011-08-26 12:20:00 +0000
+++ lib/crypto-base.sh	2011-09-07 22:27:14 +0000
@@ -82,7 +82,7 @@ crypto_prepare () {
 	if [ "$method" = swap ]; then
 		disable_swap "$dev" "$id"
 	fi
-	if [ "$method" != crypto ]; then
+	if [ "$method" != crypto ] && [ "$method" != crypto_keep ]; then
 		crypto_prepare_method "$id" dm-crypt || return 1
 		rm -f "$id/use_filesystem"
 		rm -f "$id/format"
@@ -820,7 +820,8 @@ crypto_check_setup() {
 			[ -f $id/crypto_type ] || continue

 			method=$(cat $id/method)
-			if [ $method != crypto ]; then
+			if [ $method != crypto ] && \
+			   [ $method != crypto_keep ]; then
 				continue
 			fi
 			type=$(cat $id/crypto_type)

=== modified file 'update.d/crypto_visuals'
--- update.d/crypto_visuals	2007-12-05 20:18:24 +0000
+++ update.d/crypto_visuals	2011-09-07 22:16:23 +0000
@@ -37,8 +37,9 @@ cryptdev_shortname ()
 	esac
 }