trustfence: extend partition encryption support to any partition

Modify the recovery and trustfence initramfs scripts to be able to encrypt any
partition on the internal storage media, not just the rootfs.

To implement this functionality, add a new recovery command called
'encrypt_partitions'. When used, this command must contain a comma-separated
list of the partitions that are to be encrypted by the end of the recovery
process, including partitions that were already encrypted beforehand. Any
partition that isn't in the list will be unencrypted. If the command is absent,
no changes will be made, but it's possible to pass an empty command to
unencrypt all partitions.

Include a blacklist to avoid encrypting partitions that shouldn't be encrypted,
such as partitions that need to be accessed by the ROM code/U-Boot or
partitions that contain encryption keys.

While at it, remove unnecessary "get_kernel_version" function from the script.

https://onedigi.atlassian.net/browse/DEL-7174

Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
This commit is contained in:
Gabriel Valcazar 2021-04-28 15:36:55 +02:00
parent c89d46600a
commit 59e652ec9b
2 changed files with 253 additions and 55 deletions

View File

@ -29,6 +29,16 @@ UPDATE_MOUNT_DIR="/mnt/update"
ROOTFS_NEEDS_FORMAT="no" ROOTFS_NEEDS_FORMAT="no"
SWUPDATE_OUTPUT="swupdate_output.txt" SWUPDATE_OUTPUT="swupdate_output.txt"
PART_LIST=""
ENC_PARTS=""
DEFAULT_ENC_PARTS="yes"
NAND_PARTS_BLACKLIST="bootloader environment linux recovery safe"
EMMC_PARTS_BLACKLIST="linux recovery safe"
ENC_DIFF=""
UNENC_DIFF=""
REBOOT_TIME=10 REBOOT_TIME=10
# Functions. # Functions.
@ -165,6 +175,34 @@ is_nand() {
fi fi
} }
#------------------------------------------------------------------------------
# Function - contains
#
# Returns true if and only if the list $1 contains $2
#------------------------------------------------------------------------------
contains() {
echo "${1}" | grep -qs "\b${2}\b"
return $?
}
#------------------------------------------------------------------------------
# Function - remove_duplicates
#
# Removes duplicate entries from a list
#------------------------------------------------------------------------------
remove_duplicates () {
echo "${1}" | tr ' ' '\n' | sort | uniq | tr '\n' ' ' | xargs
}
#------------------------------------------------------------------------------
# Function - remove_entry
#
# Removes an entry from a list
#------------------------------------------------------------------------------
remove_entry () {
echo "${1}" | sed "s/\b${2}\b//g" | xargs
}
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Function - format_partition # Function - format_partition
# #
@ -188,6 +226,10 @@ format_partition() {
# @param ${1} - UBI Volume name to format. # @param ${1} - UBI Volume name to format.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
format_ubi_volume() { format_ubi_volume() {
log "Formatting '${1}' ubi volume"
psplash_message "Formatting '${1}' partition..."
psplash_progress "0"
# Find the MTD partition. # Find the MTD partition.
local mtd_num="$(sed -ne "s/mtd\([0-9]\+\):.*\<${1}\>.*/\1/g;T;p" /proc/mtd 2>/dev/null)" local mtd_num="$(sed -ne "s/mtd\([0-9]\+\):.*\<${1}\>.*/\1/g;T;p" /proc/mtd 2>/dev/null)"
if [ -z "${mtd_num}" ]; then if [ -z "${mtd_num}" ]; then
@ -232,6 +274,10 @@ format_ubi_volume() {
# @param ${1} - Partition name to format. # @param ${1} - Partition name to format.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
format_emmc_block() { format_emmc_block() {
log "Formatting '${1}' eMMC block"
psplash_message "Formatting '${1}' partition..."
psplash_progress "0"
# Find partition block number. # Find partition block number.
local partition_block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<${1}\>.*,\1,g;T;p")" local partition_block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<${1}\>.*,\1,g;T;p")"
if [ -b "${partition_block}" ]; then if [ -b "${partition_block}" ]; then
@ -239,6 +285,15 @@ format_emmc_block() {
if grep -qs "${partition_block}" /proc/mounts; then if grep -qs "${partition_block}" /proc/mounts; then
umount "${partition_block}" >/dev/null 2>&1 umount "${partition_block}" >/dev/null 2>&1
fi fi
# If partition is encrypted, format with trustfence-tool first
if contains "${encrypt_partitions}" "${1}"; then
trustfence-tool --format ${partition_block} crypt${1}
if [ ! "$?" = "0" ]; then
quit_with_error "Error formatting '${1}' partition for encryption"
fi
partition_block="/dev/mapper/crypt${1}"
psplash_progress "50"
fi
# Format emmc block. # Format emmc block.
mkfs.ext4 "${partition_block}" >/dev/null 2>&1 mkfs.ext4 "${partition_block}" >/dev/null 2>&1
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
@ -316,15 +371,10 @@ check_swu_package() {
} }
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Function - set_encryption_flag # Function - update_mtdparts
# # Updates the encryption flags on the mtdparts environment variable
# Set the rootfs encryption flag to the mtdparts variable.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
set_encryption_flag() { update_mtdparts() {
if [ "$(is_nand)" = "no" ]; then
return
fi
# Read the mtdparts variable. # Read the mtdparts variable.
read_uboot_var mtdparts mtdparts read_uboot_var mtdparts mtdparts
@ -333,41 +383,63 @@ set_encryption_flag() {
quit_with_error "No mtdparts found" quit_with_error "No mtdparts found"
fi fi
# Parse the mtdparts value. # Add encryption flag to new encrypted partitions
case "${mtdparts}" in for p in ${ENC_DIFF}; do
*\(rootfs\)enc*) mtdparts=$(echo "${mtdparts}" | sed "s/(${p})/(${p})enc/g")
# Partition already flagged. done
;;
*\(rootfs\)*)
# Add the flag to the rootfs.
local new_mtdparts=$(echo "${mtdparts}" | sed "s/(rootfs)/(rootfs)enc/g")
set_uboot_var mtdparts "${new_mtdparts}"
;;
*)
quit_with_error "Error flagging rootfs as encrypted"
;;
esac
if [ -n "${update_package_bool}" ]; then # Remove encryption flag from unencrypted partitions
# Modify the recovery command not to set again the key. for p in ${UNENC_DIFF}; do
new_command=$(echo "${COMMAND}" | sed "s/encryption_key=[^ ]*//g") mtdparts=$(echo "${mtdparts}" | sed "s/(${p})enc/(${p})/g")
set_uboot_var "${ENV_RECOVERY_COMMAND}" "${new_command}" done
psplash_progress "100"
reboot_system set_uboot_var mtdparts "${mtdparts}"
fi
} }
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# Function - get_kernel_version # Function - parse_partition_info
# #
# Obtain the kernel version and return it in number format # Obtain a list of all partitions and a list of encrypted partitions
#
# @return - kernel version
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
get_kernel_version() { parse_partition_info() {
major="$(uname -r | cut -d'.' -f1)" if [ "$(is_nand)" = "yes" ]; then
minor="$(uname -r | cut -d'.' -f2)" # Read the mtdparts variable.
echo $(((major << 8) + minor)) read_uboot_var mtdparts mtdparts
if [ -z "${mtdparts}" ]; then
quit_with_error "No mtdparts found"
fi
TMP_LIST=$(echo ${mtdparts} | cut -d ':' -f2 | tr "," " ")
for p in ${TMP_LIST}; do
PART=$(echo $p | sed 's,.*(\(.*\)).*,\1,')
PART_LIST="${PART_LIST} ${PART}"
echo $p | grep -qs ")enc" && ENC_PARTS="${ENC_PARTS} ${PART}"
done
else
PART_LIST=$(fdisk -l /dev/mmcblk0 | grep '^ *' | rev | cut -d ' ' -f1 | rev)
# If the partition list doesn't exist, we can consider it empty
read_uboot_var encrypted_parts_list ENC_PARTS
ENC_PARTS=$(remove_duplicates "${ENC_PARTS}")
fi
# If a new partition list wasn't explicitly passed in the recovery
# command, assume the list hasn't changed. This prevents having to
# always pass the list even if it doesn't have any changes.
[ "${DEFAULT_ENC_PARTS}" = "yes" ] && encrypt_partitions="${ENC_PARTS}"
}
#------------------------------------------------------------------------------
# Function - get_blacklist
#
# Return the list of partitions that can't be encrypted on the storage media
#------------------------------------------------------------------------------
get_blacklist() {
if [ "$(is_nand)" = "yes" ]; then
echo "${NAND_PARTS_BLACKLIST}"
else
echo "${EMMC_PARTS_BLACKLIST}"
fi
} }
# Main # Main
@ -440,17 +512,73 @@ for arg in ${COMMAND}; do
update_package=*) update_package=*)
update_package_bool=true; update_package_bool=true;
eval "${arg}";; eval "${arg}";;
encrypt_partitions=*)
eval "${arg}";
DEFAULT_ENC_PARTS="no";
encrypt_partitions=$(echo ${encrypt_partitions} | tr "," " ");
encrypt_partitions=$(remove_duplicates "${encrypt_partitions}");;
wipe_ubi_partitions=*)
eval "${arg}";
wipe_ubi_partitions=$(echo ${wipe_ubi_partitions} | tr "," " ");
wipe_ubi_partitions=$(remove_duplicates "${wipe_ubi_partitions}");;
*) *)
# Not supported command # Not supported command
quit_with_error "Unknown recovery command '${arg}'";; quit_with_error "Unknown recovery command '${arg}'";;
esac esac
done done
# Get current partition information
parse_partition_info
# Sanitize the encrypted partition list
BLACKLISTED=""
for p in ${encrypt_partitions}; do
contains "${PART_LIST}" "${p}" || quit_with_error "Cannot encrypt nonexistant partition '${p}'"
# Take note of any blacklisted partitions to remove them from the list later
contains "$(get_blacklist)" "${p}" && BLACKLISTED="${BLACKLISTED} ${p}"
done
for p in ${BLACKLISTED}; do
log_warning "Encryption of partition '${p}' is forbidden, skipping"
encrypt_partitions=$(remove_entry "${encrypt_partitions}" "${p}")
done
# Sanity checks. # Sanity checks.
if [ -n "${update_package_bool}" ]; then if [ -n "${update_package_bool}" ]; then
check_swu_package "${update_package}" check_swu_package "${update_package}"
fi fi
# Format UBI partitions
if [ "$(is_nand)" = "yes" -a -n "${wipe_ubi_partitions}" ]; then
for p in ${wipe_ubi_partitions}; do
# To protect against manually injected commands, only format
# partitions that exist and aren't blacklisted.
contains "${PART_LIST}" "${p}" && \
! contains "${NAND_PARTS_BLACKLIST}" "${p}" && \
format_ubi_volume "${p}"
done
fi
# Compare the current and new encrypted partition lists and take note of the
# differences, if any. If we know the list hasn't changed, don't bother
# checking.
if [ "${DEFAULT_ENC_PARTS}" = "no" ]; then
# Iterate through the old encrypted partition list
for p in ${ENC_PARTS}; do
if ! contains "${encrypt_partitions}" "${p}"; then
# Partition is no longer in the list, unencrypt it
UNENC_DIFF="${UNENC_DIFF} ${p}"
fi
done
# Iterate through the new encrypted partition list
for p in ${encrypt_partitions}; do
if ! contains "${ENC_PARTS}" "${p}"; then
# Partition is new in the list, encrypt it
ENC_DIFF="${ENC_DIFF} ${p}"
fi
done
fi
# Check if encryption key command is configured. # Check if encryption key command is configured.
if [ -n "${encryption_key_bool}" ]; then if [ -n "${encryption_key_bool}" ]; then
log "Trustfence encryption key setup requested (new key: ${encryption_key:-random})" log "Trustfence encryption key setup requested (new key: ${encryption_key:-random})"
@ -459,22 +587,65 @@ if [ -n "${encryption_key_bool}" ]; then
trustfence-tool "--newkey${encryption_key:+=${encryption_key}}" trustfence-tool "--newkey${encryption_key:+=${encryption_key}}"
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
psplash_progress "10" psplash_progress "100"
log "Trustfence encryption key setup succeed!" log "Trustfence encryption key setup succeed!"
else else
quit_with_error "Error configuring trustfence encryption key" quit_with_error "Error configuring trustfence encryption key"
fi fi
fi
# Set the encryption flag to the rootfs. # Proceed with the formatting if any partitions require it
set_encryption_flag if [ -n "${encryption_key_bool}" -a -n "${encrypt_partitions}" ] || [ -n "${ENC_DIFF}" -o -n "${UNENC_DIFF}" ]; then
log "Proceeding to format partitions"
# Format partition. # On NAND devices, if the list has changed, reflect the change in the
if [ "$(is_nand)" = "no" ]; then # environment now so that the MTD driver loads the partitions with
psplash_message "Formatting rootfs partition..." # their new encryption status on the second run of the recovery script.
rootfs_block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<rootfs\>.*,\1,g;T;p")" if [ "$(is_nand)" = "yes" ] && [ -n "${ENC_DIFF}" -o -n "${UNENC_DIFF}" ]; then
trustfence-tool --format ${rootfs_block} cryptroot update_mtdparts
fi
# Format all currently encrypted partitions with the new encryption key
if [ -n "${encryption_key_bool}" ]; then
ENC_DIFF="${encrypt_partitions}"
fi
if [ "$(is_nand)" = "yes" ]; then
psplash_message "Preparing new recovery command..."
psplash_progress "0"
# Remove the encrypted parts list from the recovery command.
new_command="${COMMAND}"
new_command=$(echo "${new_command}" | sed "s/encrypt_partitions=[^ ]*//g")
if [ -n "${encryption_key_bool}" ]; then
# Modify the recovery command to not set the key again.
new_command=$(echo "${new_command}" | sed "s/encryption_key=[^ ]*//g")
fi
# Append the list of partitions to format to the recovery command.
wipe_ubi_partitions=$(echo "${ENC_DIFF} ${UNENC_DIFF}" | tr " " ",")
new_command="${new_command} wipe_ubi_partitions=${wipe_ubi_partitions}"
set_uboot_var "${ENV_RECOVERY_COMMAND}" "${new_command}"
log "Rebooting to complete partition formatting..."
psplash_progress "100"
reboot_system
else
# On eMMC devices, update the new encrypted partition list
# gradually using the old list as a base. By the end, the list
# should be the same as the new one.
for p in ${ENC_DIFF}; do
format_emmc_block ${p}
ENC_PARTS=$(remove_duplicates "${ENC_PARTS} ${p}")
set_uboot_var encrypted_parts_list "${ENC_PARTS}"
done
for p in ${UNENC_DIFF}; do
format_emmc_block ${p}
ENC_PARTS=$(remove_entry "${ENC_PARTS}" "${p}")
set_uboot_var encrypted_parts_list "${ENC_PARTS}"
done
fi fi
psplash_progress "100"
fi fi
# Check if update package command is configured. # Check if update package command is configured.
@ -511,8 +682,6 @@ fi
# Check if wipe update patition command is configured. # Check if wipe update patition command is configured.
if [ -n "${wipe_update_bool}" ]; then if [ -n "${wipe_update_bool}" ]; then
log "Wipe 'update' partition requested" log "Wipe 'update' partition requested"
psplash_message "Erasing update partition..."
psplash_progress "0"
format_partition update format_partition update
fi fi

View File

@ -17,6 +17,10 @@
HALT_TIME="10" HALT_TIME="10"
read_uboot_var() {
eval "${2}=\"$(fw_printenv -n ${1} 2>/dev/null)\""
}
error() { error() {
[ "${#}" != "0" ] && printf "\n[ERROR]: %s\n\n" "${1}" [ "${#}" != "0" ] && printf "\n[ERROR]: %s\n\n" "${1}"
echo "The system will halt in ${HALT_TIME} seconds" echo "The system will halt in ${HALT_TIME} seconds"
@ -34,6 +38,9 @@ mount -t proc proc /proc
mount -t sysfs sysfs /sys mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev mount -t devtmpfs devtmpfs /dev
# Setup fw_printenv.
mkdir -p /var/lock
# Set kernel console loglevel # Set kernel console loglevel
LOGLEVEL="$(sysctl -n kernel.printk)" LOGLEVEL="$(sysctl -n kernel.printk)"
sysctl -q -w kernel.printk=4 sysctl -q -w kernel.printk=4
@ -56,19 +63,41 @@ if [ -n "${rescue}" ]; then
done done
fi fi
# Open LUKS encrypted device # Get encrypted partition list and remove duplicate entries
if trustfence-tool ${root} cryptroot; then read_uboot_var encrypted_parts_list ENC_PARTS
# Reset root variable to the decrypted mapped device ENC_PARTS=$(echo "${ENC_PARTS}" | tr ' ' '\n' | sort | uniq | tr '\n' ' ' | xargs)
root="/dev/mapper/cryptroot"
else for p in ${ENC_PARTS}; do
error "unable to open encrypted partition." # Translate partition name to block device
fi block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<${p}\>.*,\1,g;T;p")"
# Open LUKS encrypted device
trustfence-tool ${block} crypt${p}
if [ ! "$?" = "0" ]; then
error "unable to open encrypted partition ${p}"
fi
if [ "${p}" = "rootfs" ]; then
# Reset root variable to the decrypted mapped device
root="/dev/mapper/cryptrootfs"
elif [ "${p}" = "update" ]; then
# Mount update partition after mounting the rootfs
UPDATE="/dev/mapper/cryptupdate"
fi
done
# Mount mapped device # Mount mapped device
mkdir -p /newroot mkdir -p /newroot
FSTYPE="$(blkid ${root} | sed -e 's,.*TYPE="\([^"]\+\)".*,\1,g')" FSTYPE="$(blkid ${root} | sed -e 's,.*TYPE="\([^"]\+\)".*,\1,g')"
mount ${FSTYPE:+-t ${FSTYPE}} ${root} /newroot mount ${FSTYPE:+-t ${FSTYPE}} ${root} /newroot
if [ -n "${UPDATE}" ]; then
mkdir -p /newroot/mnt/update
FSTYPE="$(blkid ${UPDATE} | sed -e 's,.*TYPE="\([^"]\+\)".*,\1,g')"
mount ${FSTYPE:+-t ${FSTYPE}} ${UPDATE} /newroot/mnt/update
fi
# #
# Clean-up and do the switch_root to the final rootfs # Clean-up and do the switch_root to the final rootfs
# #