# # This file is for inclusion with # . /lib/cryptsetup/cryptdisks.functions # and should not be executed directly. PATH="/sbin:/bin" TABFILE=/etc/crypttab CRYPTDISKS_ENABLE="Yes" #set -x # Sanity checks [ -x /sbin/cryptsetup ] || exit 0 [ -f "$TABFILE" ] || exit 0 . /lib/lsb/init-functions if [ -r /etc/default/cryptdisks ]; then . /etc/default/cryptdisks fi MOUNT="$CRYPTDISKS_MOUNT" case "$CRYPTDISKS_ENABLE" in [Nn]*) exit 0 ;; esac # Always output to console stdin=`readlink /proc/self/fd/0` if [ "${stdin#/dev/null}" != "$stdin" ] && [ "$ON_VT" != "yes" ]; then exec env ON_VT=yes /usr/bin/openvt -f -c `fgconsole` -w $0 "$@" fi # Parses the option field from the crypttab file parse_opts () { local opts opt IFS PARAM VALUE opts="$1" LOUD="" PARAMS="" CHECK="" CHECKARGS="" PRECHECK="" TRIES="3" MAKETMP="" MAKESWAP="" USELUKS="" TIMEOUT="" KEYSCRIPT="" # Parse the options field, convert to cryptsetup parameters # and construct the command line IFS=',' for opt in $opts; do PARAM=$(echo "$opt" | sed 's/=.*//') VALUE=$(echo "$opt" | sed '/=/!d;s/^.*=//') case "$PARAM" in readonly) PARAMS="$PARAMS -r" ;; cipher) if [ -z "$VALUE" ]; then log_warning_msg "$dst: no value for cipher option, skipping" return 1 fi PARAMS="$PARAMS -c $VALUE" ;; size) if [ -z "$VALUE" ]; then log_warning_msg "$dst: no value for size option, skipping" return 1 fi PARAMS="$PARAMS -s $VALUE" ;; hash) if [ -z "$VALUE" ]; then log_warning_msg "$dst: no value for hash option, skipping" return 1 fi PARAMS="$PARAMS -h $VALUE" ;; verify) PARAMS="$PARAMS -y" ;; check) if [ -z "$VALUE" ]; then VALUE="$CRYPTDISKS_CHECK" fi if [ -x "$VALUE" ]; then CHECK="$VALUE" elif [ -x "/lib/cryptsetup/checks/$VALUE" ]; then CHECK="/lib/cryptsetup/checks/$VALUE" else log_warning_msg "check $VALUE is not an executable script, skipping" return 1 fi ;; checkargs) if [ -n "$VALUE" ]; then CHECKARGS="$VALUE" fi ;; precheck) if [ -z "$VALUE" ]; then VALUE="$CRYPTDISKS_PRECHECK" fi if [ -x "$VALUE" ]; then PRECHECK="$VALUE" elif [ -x "/lib/cryptsetup/checks/$VALUE" ]; then PRECHECK="/lib/cryptsetup/checks/$VALUE" else log_warning_msg "precheck $VALUE is not an executable script, skipping" return 1 fi ;; tries) if echo "$VALUE" | grep -q "^[[:digit:]]\+$" && [ "$VALUE" -gt 0 ]; then TRIES="$VALUE" else log_warning_msg "$dst: option tries used with an incorrect argument - forced to $TRIES" fi PARAMS="$PARAMS --tries=$TRIES" ;; timeout) if [ -z "$VALUE" ]; then TIMEOUT="$CRYPTDISKS_TIMEOUT" elif echo "$VALUE" | grep -q "^[[:digit:]]\+$"; then TIMEOUT="$VALUE" else log_warning_msg "$dst: option timeout used with an incorrect argument - forced to '$TIMEOUT'" fi PARAMS="$PARAMS --timeout=$TIMEOUT" ;; swap) MAKESWAP=yes SWCHECK="/lib/cryptsetup/checks/un_vol_id" SWCHECKARGS="swap" ;; tmp) MAKETMP=yes ;; luks) USELUKS=yes ;; loud) LOUD=yes ;; keyscript) if [ -n "$KEYSCRIPT" ]; then log_warning_msg "$dst: multiple key decryption options are not allowed together, skipping" return 1 elif [ -z "$VALUE" ]; then log_warning_msg "$dst: no value for keyscript option, skipping" return 1 fi KEYSCRIPT="$VALUE" ;; esac done return 0 } # Set up loopback devices lo_setup () { local loopdev if [ ! -f "$src" ]; then return 0 fi if [ ! -x /sbin/losetup ]; then return 1 fi if ! grep -q "[[:space:]]loop$" /proc/devices; then modprobe -qb loop > /dev/null 2>&1 || return 1 fi loopdev=$(losetup -f 2> /dev/null) || return 1 losetup "$loopdev" "$src" || return 1 src="$loopdev" return 0 } # Sanity check for keys check_key () { local GMODE OMODE OWNER GROUP # If the keyscript option is set, the "key" is just an argument to # the keyscript and not necessarily a file if [ -n "$KEYSCRIPT" ]; then return 0 fi if [ -z "$key" ] || [ "$key" = "none" ]; then INTERACTIVE="yes" return 0 fi INTERACTIVE="no" if [ ! -e "$key" ]; then log_warning_msg "$dst: keyfile not found" return 1 fi # stat is unfortunately in /usr/bin... OMODE=$(ls -l "$key" | sed 's/[[:space:]].*//;s/^.\{7\}//') GMODE=$(ls -l "$key" | sed 's/[[:space:]].*//;s/^.\{4\}\(.\{3\}\).*/\1/') GROUP=$(ls -l "$key" | sed 's/^.\{11\}[^[:space:]]* [^[:space:]]* \([^[:space:]]*\).*/\1/') OWNER=$( ls -l "$key" | sed 's/^.\{11\}[^[:space:]]* \([^[:space:]]*\).*/\1/') # LUKS requires a persistent key, /dev/*random is not supported if [ "$USELUKS" = "yes" ] && [ "$key" != "${key%random}" ]; then log_warning_msg "$dst: LUKS does not work with random data as key" return 1 fi # Check owner if [ "$OWNER" != "root" ]; then log_warning_msg "$dst: INSECURE OWNER FOR $key, see /usr/share/doc/cryptsetup/README.Debian." fi # If key is random, we're done if [ "$key" != "${key%random}" ]; then return 0 fi # Check group and other permissions if [ "$OMODE" != "---" ] || [ "$GROUP" != "root" ] && [ "$GMODE" != "---" ]; then log_warning_msg "$dst: INSECURE MODE FOR $key, see /usr/share/doc/cryptsetup/README.Debian." fi return 0 } # Setup a luks mapping do_luks () { local tried tried=0 if ! cryptsetup isLuks "$src" >/dev/null 2>&1; then log_warning_msg "$dst: device '$src' is not a LUKS partition, skipping" return 1 fi if [ -n "$KEYSCRIPT" ]; then PARAMS="$PARAMS --key-file=-" while [ "$tried" -lt "$TRIES" ]; do if "$KEYSCRIPT" "$key" <&1 | cryptsetup $PARAMS luksOpen "$src" "$dst"; then break fi tried=$(( $tried + 1 )) done elif [ "$INTERACTIVE" != "yes" ]; then PARAMS="$PARAMS --key-file=$key" while [ "$tried" -lt "$TRIES" ]; do if cryptsetup $PARAMS luksOpen "$src" "$dst" <&1; then break fi tried=$(( $tried + 1 )) done else if [ -p /dev/.initramfs/usplash_outfifo ] && [ -x /sbin/usplash_write ]; then while [ "$tried" -lt "$TRIES" ]; do usplash_write "INPUTQUIET Enter password to unlock the disk ($dst): " PASS="$(cat /dev/.initramfs/usplash_outfifo)" echo -n "$PASS" | cryptsetup $PARAMS luksOpen "$src" "$dst" >/dev/null 2>&1 if [ "$?" -eq "0" ]; then break fi tried=$(( $tried + 1 )) done usplash_write "TEXT-URGENT " else cryptsetup $PARAMS luksOpen $src $dst <&1 fi fi if [ "$tried" -ge "$TRIES" ]; then return 1 fi if [ -n "$CHECK" ] && ! "$CHECK" "/dev/mapper/$dst" $CHECKARGS; then log_warning_msg "$dst: the check for '/dev/mapper/$dst' failed" cryptsetup luksClose $dst return 1 fi return 0 } # Setup a regular mapping do_noluks () { local pre_out tried tried=0 if [ -z "$PRECHECK" ]; then PRECHECK="/lib/cryptsetup/checks/un_vol_id" fi if ! pre_out=$("$PRECHECK" "$src" 2> /dev/null) && \ [ "$MAKESWAP" != "yes" ] && \ ! /lib/cryptsetup/checks/vol_id "$src" swap >/dev/null; then log_warning_msg "$dst: the precheck for '$src' failed: $pre_out" return 1 fi if [ -n "$KEYSCRIPT" ]; then PARAMS="$PARAMS --key-file=-" elif [ "$INTERACTIVE" != "yes" ]; then PARAMS="$PARAMS --key-file=$key" fi while [ "$tried" -lt "$TRIES" ]; do if [ -n "$KEYSCRIPT" ]; then "$KEYSCRIPT" "$key" <&1 | cryptsetup $PARAMS create "$dst" "$src" else cryptsetup $PARAMS create "$dst" "$src" <&1 fi if [ -z "$CHECK" ] || "$CHECK" "/dev/mapper/$dst" $CHECKARGS; then break else log_warning_msg "$dst: the check for '/dev/mapper/$dst' failed - maybe the password is wrong" cryptsetup remove "$dst" fi tried=$(( $tried + 1 )) done if [ "$tried" -ge "$TRIES" ]; then return 1 fi return 0 } # Premounts file systems mount_fs () { local point MOUNTED="" for point in $MOUNT; do if mount "$point" >/dev/null; then MOUNTED="$MOUNTED $point" fi done } # Postunmounts file systems umount_fs () { local point for point in $MOUNTED; do umount "$point" >/dev/null done } # Prepares swap partitions using random keys do_swap () { local swap_out if [ "$MAKESWAP" != "yes" ] || [ ! -b "/dev/mapper/$dst" ]; then return 0 fi if swap_out=$(/lib/cryptsetup/checks/un_vol_id "/dev/mapper/$dst" 2> /dev/null) || \ /lib/cryptsetup/checks/vol_id "/dev/mapper/$dst" swap > /dev/null 2>&1; then mkswap "/dev/mapper/$dst" > /dev/null 2>&1 else log_warning_msg "$dst: the check for '/dev/mapper/$dst' failed. /dev/mapper/$dst contains data: $swap_out" do_close return 1 fi return 0 } # Prepares tmp partitions using random keys do_tmp () { if [ "$MAKETMP" != "yes" ] || [ ! -b "/dev/mapper/$dst" ]; then return 0 fi mke2fs "/dev/mapper/$dst" > /dev/null 2>&1 || return 1 mount -t ext2 "/dev/mapper/$dst" /tmp || return 1 chmod 1777 /tmp umount /tmp return 0 } # Removes a mapping do_close () { local found IFS opt found="no" IFS=',' for opt in $opts; do if [ "$opt" = "luks" ]; then found="yes" break fi done if [ "$found" = "yes" ]; then cryptsetup luksClose "$dst" else cryptsetup remove "$dst" fi return $? } load_optimized_aes_module () { local asm_module modulesdir # find directory with kernel modules modulesdir="/lib/modules/`uname -r`" # Add assembly optimized AES module if it exists asm_module=`ls -1 "$modulesdir"/kernel/arch/*/*/aes*.ko` if [ $asm_module ];then insmod $asm_module 2>/dev/null || true fi } # Sets up all entries in crypttab do_start () { local dst src key opts result modprobe -qb dm-mod || true modprobe -qb dm-crypt || true dmsetup mknodes > /dev/null 2>&1 || true log_action_begin_msg "Starting $INITSTATE crypto disks" load_optimized_aes_module mount_fs egrep -v "^[[:space:]]*(#|$)" "$TABFILE" | while read dst src key opts; do # Make sure that all fields are present if [ -z "$dst" ]; then continue elif [ -z "$src" ] || [ -z "$key" ] || [ -z "$opts" ]; then device_msg "$dst" "skipped, missing parameters" continue fi # parse UUID= symlinks if [ ${src#UUID=} != $src ]; then src="/dev/disk/by-uuid/${src#UUID=}" elif [ ${src#LABEL=} != $src ]; then src="/dev/disk/by-label/${src#LABEL=}" fi # Make sure source device exists if [ ! -r "$src" ]; then if [ "$LOUD" = "yes" ]; then device_msg "$dst" "skipped, device $src does not exist" fi continue fi # Make sure that target device doesn't exist if [ -b "/dev/mapper/$dst" ]; then device_msg "$dst" "running" continue fi # All checks passed, do the preparatory steps if ! parse_opts "$opts"; then device_msg "$dst" "invalid opts" continue elif ! check_key; then device_msg "$dst" "invalid key" continue elif ! lo_setup; then device_msg "$dst" "loopback failed" fi # Do the real setup log_progress_msg "$dst (starting)" result="ok" if [ "$USELUKS" = "yes" ]; then do_luks || result="fail" else do_noluks || result="fail" fi # Finish up if [ "$result" != "ok" ]; then log_progress_msg "$dst (failed)" else do_swap do_tmp log_progress_msg "$dst (started)" fi done umount_fs log_action_end_msg 0 } # Removes all mappings in crypttab do_stop () { local dst src key opts opencount major minor loopmajor dmsetup mknodes log_action_begin_msg "Stopping $INITSTATE crypto disks" loopmajor=$(grep "[[:space:]]*loop$" /proc/devices | sed 's/^[[:space:]]*//;s/[[:space:]].*//') egrep -v "^[[:space:]]*(#|$)" "$TABFILE" | while read dst src key opts; do if [ ! -b "/dev/mapper/$dst" ]; then device_msg "$dst" "stopped" continue fi opencount=$(dmsetup info -c --noheadings -o open "$dst" 2> /dev/null) if [ -z "$opencount" ]; then device_msg "$dst" "error" continue elif [ "$opencount" != "0" ]; then device_msg "$dst" "busy" continue fi major=$(dmsetup info -c --noheadings -o major "$dst" 2> /dev/null) minor=$(dmsetup info -c --noheadings -o minor "$dst" 2> /dev/null) if [ -z "$major" ] || [ -z "$minor" ]; then device_msg "$dst" "error" continue fi do_close log_action_cont_msg "$dst (stopping)" # Detach loopback device, if attached if [ -f "$src" ] && [ -n "$loopmajor" ] && [ "$loopmajor" = "$major" ]; then losetup -d "/dev/loop$minor" > /dev/null 2>&1 || true fi done log_action_end_msg 0 } # Convenience function to handle $VERBOSE device_msg () { local dst msg dst="$1" msg="$2" if [ "$VERBOSE" != "no" ]; then log_action_cont_msg "$dst ($msg)" fi }