#!/bin/bash # Copyright (C) 1996-2007 SUSE Linux Products GmbH, Nuernberg, Germany. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Author: Werner Fink , 2001-2006 # Chris Rivera 2006 # Ludwig Nussel 2007 # # /etc/init.d/boot.crypto # ### BEGIN INIT INFO # Provides: boot.crypto # Required-Start: boot.localfs boot.device-mapper # Should-Start: # Required-Stop: # Default-Start: B # Default-Stop: # Description: Enable crypto file systems before leaving boot phase # Short-Description: Enable crypto file systems before leaving boot phase ### END INIT INFO # Determine the base and follow a runlevel link name. base=${0##*/} link=${base#*[SK][0-9][0-9]} . /etc/rc.status trap "echo" SIGINT SIGSEGV set +e # Redirect to real device (e.g. in case of boot logging) : ${CRYPTOTAB:=/etc/cryptotab} : ${CRYPTTAB:=/etc/crypttab} : ${TIMEOUT:=120} if test -z "$REDIRECT" ; then if (echo -n > /dev/tty) 2>/dev/null ; then REDIRECT=/dev/tty else REDIRECT=/dev/console fi fi splash="" did_redirect="" otty=$(stty -g) redirect () { test -z "$did_redirect" || return 0 did_redirect=1 if test -e /proc/splash ; then read splash < /proc/splash echo verbose > /proc/splash fi test -z "$otty" || stty "$otty" < $REDIRECT; stty -nl -ixon ignbrk -brkint < $REDIRECT if test -x /etc/init.d/kbd -a -n "$RUNLEVEL" ; then /etc/init.d/kbd start fi } restore () { test -n "$did_redirect" || return 0 test -z "$otty" || stty "$otty" < $REDIRECT; [[ "$splash" =~ silent ]] && echo silent > /proc/splash } ppid=0 prmt="" setprompt () { if test -t 1 -a "$TERM" != "raw" -a "$TERM" != "dumb" && stty size <&1 > /dev/null 2>&1 then ( trap "exit 0" SIGTERM trap "echo" SIGINT SIGSEGV usleep 15000 while test $timeout -gt 0 ; do # cursor to start of line, erase line, print prompt echo -en "\r\e[2K${prmt}" sleep 2 : $((timeout-=2)) done ) & ppid=$! else usleep 15000 echo -en "\r${prmt}" ppid=0 fi } unsetprompt () { local ret=$? test $ppid -gt 0 && kill -15 $ppid ppid=0 return $ret } reverse () { local _line while read -r _line ; do case "$_line" in \#*|"") continue ;; esac reverse echo "$_line" break done } detachloopdev () { if [ -n "$loopdev" ]; then losetup -d $loopdev &> /dev/null || true fi } # # Check for valid super blocks # check_superblock() { local fs="$1" local device="$2" case "$fs" in ext[23]) tune2fs -l $device &> /dev/null ;; reiserfs) debugreiserfs $device &> /dev/null ;; *) true ;; esac } # # parameters: filesys device mountpoint physdev quiet # return value: # 0 - ok # 1 - enter passphrase again # 2 - skip entry # 3 - fsck # paranoid_safety_checks() { local filesys device mp physdev quiet choice filesys="$1" device="$2" mp="$3" physdev="$4" quiet="$5" check_superblock "$filesys" "$device" if test $? -gt 0 || ! mount -t "$filesys" -n -o ro "$device" "$mp" &> /dev/null ; then umount -n "$mp" &> /dev/null || true if test -n "$quiet"; then return 2 fi echo "${warn}An error occured. Maybe the wrong passphrase was" echo "entered or the file system on $physdev is corrupted.${norm}" while true ; do # TODO: rephrase that echo "${extd}Do you want to retry entering the passphrase?${norm}" echo -n "${extd}Alternatively you may continue with a file system check.${norm}" choice= read -p " ([${extd}yes${norm}]/${extd}no${norm}/${extd}check${norm}/) " choice < $REDIRECT || { echo 'no'; choice='no'; } case "$choice" in [yY][eE][sS]|[yY]|"") return 1 ;; [nN][oO]|[nN]) return 2 ;; [Cc][hH][eE][Cc][kK]|[Cc]) return 3 ;; esac done else umount -n "$mp" &> /dev/null || true fi return 0 } run_fsck() { local filesys device mp physdev filesys="$1" device="$2" mp="$3" physdev="$4" # # Checking the structure on the loop device # if test -x /sbin/fsck.$filesys; then fsck -T -a -t $filesys $device else true fi if test $? -gt 1; then echo "${extd}fsck of $device failed. Please repair manually.${norm}" echo "${warn}Warning: do never try to repair if you have entered the wrong passphrase.${norm}" # run sulogin only during boot if test "$base" != "$link"; then PS1="(repair filesystem) # " /sbin/sulogin -t "$timeout" $REDIRECT < $REDIRECT > $REDIRECT 2>&1 sync if ! paranoid_safety_checks "$filesys" "$device" "$mp" "$physdev" 'quiet'; then echo "${extd}$physdev still appears to be damaged, skipping${norm}" /sbin/cryptsetup remove $name || true detachloopdev return 1 fi else # leave the device set up so user can fun fsck manually return 1 fi fi return 0 } report() { rc_failed "$1" shift echo -n "$*" rc_status -v } start_cryptotab () { local stat=0 local haveone='' timeout="$TIMEOUT" test -n "$tostart" || echo "Activating crypto devices using $CRYPTOTAB ... " while read loopdev physdev access filesys crypto mopt info rest ; do case "$loopdev" in \#*|"") continue ;; esac if test -n "$tostart" -a "$loopdev" != "$tostart" -a "$physdev" != "$tostart" -a "$access" != "$tostart"; then continue fi haveone=1 redirect # key length for cryptsetup keylen= ivgen='plain' hashalgo='sha512' name= # # Does the user want to skip current entry? # doskip=0 # # Does the mount point exit? # if ! test -d $access ; then report 5 "$physdev: $access doesn't exist" continue fi if ! test -e $physdev ; then report 5 "$physdev doesn't exist" continue fi # # Seeking for crypto modules # case "$crypto" in twofish) keylen=192; ivgen='null'; hashalgo='ripemd160:20' ;; twofishSL92) keylen=256; ivgen='null' ;; twofish[0-9]*) keylen=${crypto#twofish}; ;; #TODO add more algorithm or better detection *) report 2 "$physdev: unsupported algorithm \"$crypto\"" continue ;; esac cipher=twofish-cbc-$ivgen name="${loopdev#/dev/}" name="cryptotab_${name//[^A-Za-z0-9]/_}" if [ -e "/dev/mapper/$name" ]; then report 5 "$physdev: $name already mapped" continue fi while true; do # setup/check loop # # Restore virgin state # if [ -b "/dev/mapper/$name" ]; then /sbin/cryptsetup remove $name || true fi detachloopdev device="$loopdev" # # Setting up loop device # if test -n "$info" ; then prmt="${extd}Please enter passphrase for \"$info\" ($physdev): ${norm}" else prmt="${extd}Please enter passphrase for $physdev: ${norm}" fi # we always use a loop device to avoid block size issues # with e.g. cdroms for backward compatability if ! losetup $loopdev $physdev; then report 1 "$physdev..." continue 2 fi params="-t $timeout -c $cipher -s $keylen -h $hashalgo" setprompt /sbin/cryptsetup $params create "$name" "$device" < $REDIRECT > $REDIRECT 2>&1 stat=$? unsetprompt if test "$stat" -ne 0; then detachloopdev report 1 "$physdev..." continue 2 fi device="/dev/mapper/$name" paranoid_safety_checks "$filesys" "$device" "$access" "$physdev" stat="$?" if test "$stat" = 1; then # retype passphrase continue elif test "$stat" = 2; then # skip entry doskip=1 fi break done # # Does the user want to skip this entry? # if test $doskip -gt 0 ; then /sbin/cryptsetup remove $name || true report 5 "$physdev..." detachloopdev continue fi if ! run_fsck "$filesys" "$device" "$access" "$physdev"; then report 1 "$physdev..." continue fi case "$mopt" in default|"") mopt="" ;; esac mount -t $filesys ${mopt:+-o $mopt} $device $access stat=$? if test $stat -gt 0 ; then stat=1 /sbin/cryptsetup remove $name || true detachloopdev fi report $stat "$physdev..." done < $CRYPTOTAB if test -z "$haveone" -a -z "$tostart"; then rc_failed 6 rc_status -v1 fi } hashalotcryptsetup() { /sbin/hashalot ${halgo:+$halgo} -t $timeout ${pseed:+-s $pseed} ${itercountk:+-C $itercountk} | /sbin/cryptsetup "$@" } start_crypttab () { local stat=0 local haveone='' test -n "$tostart" || echo "Activating crypto devices using $CRYPTTAB ... " while read name physdev keyfile options dummy; do case "$name" in \#*|"") continue ;; esac if test -n "$tostart" -a "$name" != "$tostart" -a "$physdev" != "$tostart"; then continue fi haveone=1 redirect # skip mapped entries if test -e /dev/mapper/$name; then report 5 "$physdev: $name already mapped" continue fi test "$keyfile" = "none" && keyfile="" test "$options" = "none" && options="" # make sure the keyfile exists if test -n "$keyfile" -a ! -e "$keyfile"; then report 5 "$physdev: $keyfile does not exist" continue fi if ! test -e $physdev ; then report 5 "$physdev doesn't exist" continue fi # parse the options field skip="" params="" makeswap="" maketmp="" noauto="" luks="" check="" checkargs="" loopdev="" param_ro="" cipher="" keysize="" halgo="" timeout="" tries="" pseed="" itercountk="" while test -n "$options"; do arg=${options%%,*} options=${options##$arg} options=${options##,} param=${arg%%=*} value=${arg##$param=} case "$param" in cipher) if test -z "$value" ; then echo $"$dst: no value for cipher option, skipping" skip="yes" fi cipher="$value" ;; size) if test -z "$value" ; then echo $"$dst: no value for size option, skipping" skip="yes" fi keysize="$value" ;; hash) if test -z "$value" ; then echo $"$dst: no value for hash option, skipping" skip="yes" fi halgo="$value" ;; verify) params="$params -y" ;; swap) makeswap="yes" ;; tmp) maketmp="yes" ;; noauto) noauto="yes" ;; luks) luks="yes" ;; loop) loopdev="yes" ;; readonly) param_ro="-r" ;; timeout) case "$value" in [0-9]*) timeout="$value";; *) echo "invalid timeout '$value' ignored" ;; esac ;; tries) if test -n "$value" ; then params="$params --tries=$tries" fi ;; check) if test -n "$value" -a -x /lib/cryptsetup/checks/"$value" ; then check="/lib/cryptsetup/checks/$value" else check="/lib/cryptsetup/checks/vol_id" fi ;; checkargs) if test -n "$value" ; then checkargs="$value" fi ;; pseed) pseed="$value" ;; itercountk) itercountk="$value" ;; precheck|loud|ssl|gpg|keyscript|*) echo "unsupported crypttab option: '$param'" skip='yes' ;; esac done if test -n "$luks"; then if test -n "$cipher" -o -n "$halgo" -o -n "$keysize" -o -n "$pseed" -o -n "$itercountk"; then echo "cipher, hash, size, pseed and itercountk options are ignored for LUKS" fi fi if test -n "$keyfile"; then if test -n "$halgo" -o -n "$pseed" -o -n "$itercountk"; then report 2 "${ext}hash, pseed and itercountk options are invalid when using a key file${norm}" continue fi fi if test "$skip" = "yes" -o \( "$noauto" = "yes" -a -z "$tostart" \); then report 5 "$physdev" continue fi if test -n "$param_ro"; then params="$params $param_ro" fi # we always want a timeout to prevent accidential hanging boot if test -z "$timeout"; then timeout="$TIMEOUT" fi params="$params --timeout=$timeout" device="$physdev" # if device is a regular file then it's an image if test -n "$loopdev" -o -f $device; then loopdev='' for i in 0 1 2 3 4 5 6 7; do if ! test -b "/dev/loop$i"; then continue fi unset loopsize read loopsize < /sys/block/loop$i/size if test $? -eq 0 -a "$loopsize" = '0' && /sbin/losetup $param_ro "/dev/loop$i" $device; then device="/dev/loop$i" loopdev="$device" break fi done if test -z "$loopdev"; then report 1 "$physdev: failed to find a usable loop device" continue fi fi doskip=0 while true; do # setup/check loop if [ -b "/dev/mapper/$name" ]; then /sbin/cryptsetup remove $name fi if test -z "$keyfile"; then prmt="${extd}Please enter passphrase for $physdev ($name): ${norm}" setprompt fi # map the devices if test -n "$luks" || cryptsetup isLuks "$device" &> /dev/null; then luks='yes' /sbin/cryptsetup $params ${keyfile:+-d $keyfile} luksOpen "$device" "$name" < $REDIRECT &> $REDIRECT stat=$? else # XXX hack if test -n "$pseed" -o -n "$itercountk"; then params="$params -d /dev/stdin" # cannot use "-" as cryptsetup would hash that cryptsetup=hashalotcryptsetup else cryptsetup=/sbin/cryptsetup fi params="$params ${cipher:+-c $cipher} ${halgo:+-h $halgo} ${keysize:+-s $keysize}" $cryptsetup $params ${keyfile:+-d $keyfile} create "$name" "$device" < $REDIRECT &> $REDIRECT stat=$? unset cryptsetup fi if test -z "$keyfile"; then unsetprompt fi if test $stat -ne 0; then report 1 "$physdev... " doskip=1 detachloopdev break fi # run check if it's set if test -n "$check"; then $check "/dev/mapper/$name" $checkargs if test $? -ne 0; then report 1 "$physdev... " doskip=1 /sbin/cryptsetup remove $name detachloopdev break fi fi mountpoint='' fs_type='auto' infstab='' read dummy mountpoint fs_type dummy < <(/bin/awk -vd=/dev/mapper/$name '$1==d{print;exit}' < /etc/fstab) if test "$?" -eq 0; then infstab='yes' fi # run mkfs if the tmp option was given if test "$maketmp" = "yes"; then echo "Creating filesystem on /dev/mapper/$name..." if test "$fs_type" = 'auto'; then fs_type='ext2' fi /sbin/mkfs -t "$fs_type" /dev/mapper/$name if test $? -ne 0; then report 1 "$phsdev: failed to create temporary file system." doskip=1 /sbin/cryptsetup remove $name detachloopdev break fi fi # no need for paranoid checks with luks as cryptsetup is able to # verify the passphrase itself then # # if the device is not in fstab we can't do paranoid checks as we # don't know what the intention of the device is if test "$makeswap" != 'yes' -a -z "$luks" -a -n "$infstab"; then if test -z "$luks"; then paranoid_safety_checks "$fs_type" "/dev/mapper/$name" "$mountpoint" "$physdev" stat="$?" if test "$stat" = 1; then # retype passphrase continue elif test "$stat" = 2; then # skip entry report 5 "$physdev..." doskip=1 fi fi fi break; done # setup/check loop if test "$doskip" -gt 0; then if test -b "/dev/mapper/$name"; then /sbin/cryptsetup remove "$name" fi detachloopdev continue fi # run mkswap if necessary. boot.swap with enable this later if test "$makeswap" = "yes"; then mkswap "/dev/mapper/$name" stat="$?" test $stat -eq 0 || stat=1 report $stat "$physdev..." continue fi if test -z "$infstab"; then report 0 "$physdev..." continue fi if ! run_fsck "$fs_type" "/dev/mapper/$name" "$mp" "$physdev"; then report 1 "$physdev..." continue fi mount /dev/mapper/$name stat="$?" if test $stat -gt 0 ; then stat=1 /sbin/cryptsetup remove $name || true detachloopdev fi # set permissions for tmp dirs if test "$maketmp" = "yes"; then chmod 1777 $mountpoint fi report $stat "$physdev..." done < $CRYPTTAB if test -z "$haveone" -a -z "$tostart"; then rc_failed 6 rc_status -v1 fi } umount_or_swapoff() { # unmount device if /bin/grep -q "^/dev/mapper/$name[ \t]" /proc/mounts; then umount "/dev/mapper/$name" if test $? -gt 0 ; then return 1 fi elif /bin/grep -q "^/dev/mapper/$name[ \t]" /proc/swaps; then swapoff "/dev/mapper/$name" if test $? -gt 0 ; then return 1 fi fi return 0 } stop_cryptotab () { local haveone='' test -n "$tostop" || echo "Turning off crypto devices using $CRYPTOTAB ... " while read loopdev physdev access filesys crypto mopt rest ; do case "$loopdev" in \#*|"") continue ;; esac if test -n "$tostop" -a "$loopdev" != "$tostop" -a "$physdev" != "$tostop" -a "$access" != "$tostop"; then continue fi haveone=1 name="${loopdev#/dev/}" name="cryptotab_${name//[^A-Za-z0-9]/_}" if test -b "/dev/mapper/$name"; then if ! umount_or_swapoff; then report 1 "$physdev..." continue fi cryptsetup remove "$name" || rc_failed 1 fi if losetup $loopdev >/dev/null 2>&1; then losetup -d $loopdev || rc_failed 1 fi echo -n "$physdev..." rc_status -v done < <(reverse < $CRYPTOTAB) if test -z "$haveone" -a -z "$tostop"; then rc_status -v1 fi } stop_crypttab () { local haveone='' test -n "$tostop" || echo "Turning off crypto devices using $CRYPTTAB ... " while read name physdev keyfile options dummy; do case "$name" in \#*|"") continue ;; esac if test -n "$tostop" -a "$name" != "$tostop" -a "$physdev" != "$tostop"; then continue fi haveone=1 loopdev="" device="$physdev" if test -b "/dev/mapper/$name"; then if ! umount_or_swapoff; then report 1 "$physdev..." continue fi /sbin/cryptsetup remove "$name" || rc_failed 1 fi # delete the loop device while read line; do case "$line" in *\(${physdev}\)*) device=${line%%:*}; loopdev='yes' ;; esac done < <(/sbin/losetup -a) if test -n "$loopdev" && losetup $device >/dev/null 2>&1; then /sbin/losetup -d $device || rc_failed 1 fi echo -n "$physdev..." rc_status -v done < <(reverse < $CRYPTTAB) if test -z "$haveone" -a -z "$tostop"; then rc_status -v1 fi } status_cryptotab() { local state str local haveone='' while read loopdev physdev access filesys crypto mopt info rest ; do case "$loopdev" in \#*|"") continue ;; esac haveone=1 name="${loopdev#/dev/}" name="cryptotab_${name//[^A-Za-z0-9]/_}" echo -n "$physdev" state=0 str='' if losetup "$loopdev" > /dev/null 2>&1; then str="$str ${loopdev#/dev/}" state=$((state+1)) fi if test -b "/dev/mapper/$name"; then str="$str mapped" state=$((state+1)) fi if /bin/grep -q "^/dev/mapper/$name[ \t]" /proc/mounts; then str="$str mounted" state=$((state+1)) fi if test "$state" = 3; then rc_failed 0 elif test "$state" != 0; then rc_failed 4 else rc_failed 3 fi if test -n "$str"; then echo -n " [$str ]" fi rc_status -v done < $CRYPTOTAB if test -z "$haveone"; then report 3 "$CRYPTOTAB" fi } status_crypttab() { local state str local haveone='' while read name physdev keyfile options dummy; do case "$name" in \#*|"") continue ;; esac haveone=1 echo -n "$physdev" state=0 str='' loopdev='' # find the loop device while read line; do case "$line" in *\(${physdev}\)*) loopdev=${line%%:*};; esac done < <(/sbin/losetup -a) if test -n "$loopdev" && losetup "$loopdev" > /dev/null 2>&1; then str="$str ${loopdev#/dev/}" state=$((state|1)) fi if test -b "/dev/mapper/$name"; then str="$str mapped" state=$((state|2)) fi if /bin/grep -q "^/dev/mapper/$name[ \t]" /proc/mounts; then str="$str mounted" state=$((state|4)) elif /bin/grep -q "^/dev/mapper/$name[ \t]" /proc/swaps; then str="$str swap" state=$((state|4)) fi if test -n "$str"; then echo -n " [$str ]" fi if test "$state" != 0; then if test $((state&2)) = 0; then rc_failed 4 else if ! test -e "$physdev"; then rc_failed 1 else rc_failed 0 fi fi else rc_failed 3 fi rc_status -v done < $CRYPTTAB if test -z "$haveone"; then report 3 "$CRYPTTAB" fi } # # Cutomize_{start,stop}_hook are for interactive usage only # cutomize_start_hook () { local srv test "$base" != "$link" && return 0 test -s /etc/sysconfig/boot.crypto || return 0 . /etc/sysconfig/boot.crypto for srv in $TRY_RESTART_AT_START ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv try-restart done for srv in $RESTART_AT_START ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv restart done for srv in $RELOAD_AT_START ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv reload done } cutomize_stop_hook () { local srv test "$base" != "$link" && return 0 test -s /etc/sysconfig/boot.crypto || return 0 . /etc/sysconfig/boot.crypto for srv in $TRY_RESTART_AT_STOP ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv try-restart done for srv in $RESTART_AT_STOP ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv restart done for srv in $RELOAD_AT_STOP ; do test -n "$srv" || break test -x /etc/init.d/$srv || continue /etc/init.d/$srv reload done } rc_reset case "$1" in start|b) if ! /sbin/modprobe -q dm-crypt; then echo "kernel lacks dm-crypt support" rc_failed 5 rc_status -v rc_exit fi tostart="$2" if test -s $CRYPTOTAB; then start_cryptotab fi if test -s $CRYPTTAB; then start_crypttab fi rc_failed 0 restore cutomize_start_hook ;; stop) tostop="$2" if test -s $CRYPTOTAB; then stop_cryptotab fi if test -s $CRYPTTAB; then stop_crypttab fi rc_failed 0 cutomize_stop_hook ;; status) if test -s $CRYPTOTAB; then status_cryptotab fi if test -s $CRYPTTAB; then status_crypttab fi rc_failed 0 ;; restart) $0 stop $0 start rc_status ;; *) echo "Usage: $0 {start|stop|status|restart}" exit 1 ;; esac rc_exit