#!/bin/bash # Filename: grml-crypt # Purpose: Program to format, mount and unmount encrypted devices/files # Authors: Michael Gebetsroither # Bug-Reports: see http://grml.org/bugs/ # License: This file is licensed under the GPL v2. # Latest change: Don Jul 26 19:57:28 CEST 2007 [mika] ################################################################################ ### ### __INCLUDES ### . /etc/grml/sh-lib #. /etc/grml/sysexits-sh ### ### __VARIABLES ### verbose_=0 DEV_MAPPER_="/dev/mapper" CRYPTSETUP_="cryptsetup" IS_IMAGE_='false' SIZE_="10" SIZE_SET_='false' FSTYPE_="vfat" TARGET_="" VERIFY_PW_="" MKFS_="" DM_NAME_="" DM_PATH_="" ACTION_="" DM_PREFIX_="grml-crypt_" FORCE_='false' FSCK_='false' ENTROPY_SOURCE_='/dev/urandom' OPTIMIZED_MODE_SET_='false' OPTIMIZING_LEVEL_=0 CIPHER_SIZE_="256" CIPHER_="aes-cbc-essiv:sha256" ITERATION_TIME_="1000" ADDITIONAL_CRYPTSETUP_ARGS_="" READONLY_SET_='false' ADDITIONAL_MOUNT_ARGS_="" BATCH_MODE_="--batch-mode" ### ### __FUNCTIONS ### function printUsage { cat < [mountpoint] $PROG_NAME__ is a wrapper around cryptsetup with LUKS support to format a device OPTIONS: -s size of the loop-filesystem to create, in MB (default=$SIZE_) -t type of filesystem (default=$FSTYPE_) -r read only mode (fully supported only by start) -z insecure mode, using /dev/zero for most of the initialisation (INSECURE!) -o optimised initialisation mode (should be as secure as the default but faster) -y verifies the passphrase by asking for it twice -f force file overwriting in format mode and/or disable confirmation dialog -F only for action start: run fsck before mounting the filesystem -m additional arguments to mount -v verbose (show what is going on, v++) -h this help text CRYPTSETUP FORMAT OPTIONS: -S cipher size, could be 128, 192 or 256 (default=$CIPHER_SIZE_) -C cipher, should be aes-plain for pre-2.6.10 (default=$CIPHER_) -I iteration time spend with PBKDF2 password processing in seconds (default=$ITERATION_TIME_) -A additional arguments for cryptsetup (only supportet by format) ACTIONS: format [mountpoint] Format a device or a file (is created with the given size if it does not exist) with the given filesystem and mount it, if a mountpoint was given. start Mount the device/file in the mountpoint. stop Umount the given mountpoint (umount, luksClose, losetup -d) EOT isGrml && cat < grml-crypt with a filesystem image does ONLY work if the image is on a tmpfs (eg. in /home/grml or /tmp). EOT } function getDMName { device_="${1##*/}" # first trying normal devicename tmp_="${DM_PREFIX_}${device_}" if [ ! -e "$tmp_" ]; then echo "$tmp_" return 0 fi # second trying uuid of luks #uuid_=`execute "$CRYPTSETUP_ luksUUID $1"` #if [[ $? == 0 ]]; then # echo "$prefix_$uuid_" # return 0 #fi warn "could not create device-mapper name for $1" return 1 } function formatDevice { type_="$1" # could be donothing or init ret_=0 args_="$VERIFY_PW_ $BATCH_MODE_ --key-size $CIPHER_SIZE_ --cipher $CIPHER_ --iter-time $ITERATION_TIME_ $ADDITIONAL_CRYPTSETUP_ARGS_" #args_=`echo "$args_" |tr -s ' '` execute "$CRYPTSETUP_ $args_ luksFormat $TARGET_" warn || return 1 execute "$CRYPTSETUP_ luksOpen $TARGET_ $DM_NAME_" warn \ "could not open $DM_PATH_ to create a filesystem on it!" || return 1 if [[ $type_ == 'init' && $OPTIMIZED_MODE_SET_ == 'true' ]]; then echo "finishing optimised initialisation (this could take some time)" # FIXME execute "dd if=/dev/zero of=$DM_PATH_ bs=1M &>/dev/null" # || \ # warn "could not finish optimised initialisation properly" ret_=$? # cutted out because of no space left on device error :( #if [[ $ret_ != 0 ]]; then # execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn # return 1 #fi fi execute "$MKFS_ $DM_PATH_ >/dev/null" warn if [[ $? != 0 ]]; then execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn "could not create filesystem on $DM_PATH_" 1 return 1 else echo "Successully created $FSTYPE_ on encrypted $TARGET_" return 0 fi } function actionStart { ret_=0 # no mountpoint, by-by if [[ "$MOUNT_POINT_" == "" ]]; then printUsage die 'no mountpoint given' fi if [ ! -d "$MOUNT_POINT_" ]; then die "mountpoint $MOUNT_POINT_ does not exist" fi # removed due to unionfs problem isLuks does not work with filesystem images # without losetup #$CRYPTSETUP_ isLuks $TARGET_ || die "$TARGET_ is not a luks partition" # TARGET (is/should be) a filesystem image if [ ! -b "$TARGET_" ]; then notice "Operating on a file" isExistent "$TARGET_" die "image does not exist" TARGET_=`findNextFreeLoop` || die "could not find a free loop device" # TARGET_ is now /dev/loop execute "losetup $TARGET_ $ORIG_TARGET_" die fi cargs_="" $READONLY_SET_ && cargs_='--readonly' execute "$CRYPTSETUP_ $cargs_ luksOpen $TARGET_ $DM_NAME_" warn || execute "losetup -d $TARGET_" || \ die "could not luksOpen $TARGET_" if [[ "$FSCK_" == "true" ]] ; then execute "fsck -C $DM_NAME_" || die "fsck failed on $DM_NAME_" fi margs_="" $READONLY_SET_ && margs_='-r' execute "mount $margs_ $ADDITIONAL_MOUNT_ARGS_ $DM_PATH_ $MOUNT_POINT_" die } function actionStop { mp_="$1" ret_=0 isExistent "$mp_" die tmp_=`realpath $mp_` || die "could not get realpath of $mp_" dprint "realpath_=\"$tmp_\"" dm_path_=`mount |grep "$tmp_ "` || die "$tmp_ is not mounted" dprint "dm_path_=\"$dm_path_\"" dm_path_=`echo $dm_path_ |awk '{print $1}'` || die "could not get devicemapper name for $tmp_" dprint "dm_path_=\"$dm_path_\"" dm_name_="${dm_path_##*/}" dprint "dm_name_=\"$dm_name_\"" dmsetup info $dm_name_ >/dev/null ||die "$dm_name_ is not aktive" device_=`$CRYPTSETUP_ status $dm_name_ |awk '/device:/{print $2}'` || \ die "could not get underlying device of $dm_path_" dprint "device_=\"$device_\"" execute "umount $dm_path_" die "could not unmount $device_" execute "$CRYPTSETUP_ luksClose $dm_name_" die "could not close $dm_path_" echo "$device_" |grep loop &>/dev/null && execute "losetup -d $device_" \ die "could not delete loop device $device_" || \ execute "losetup -d $device_ &>/dev/null" eprint "could not delete loop device $device_, \ this device could possible not be a loop device => maybe bogus error" notice "$mp_ successfully unmountet/closed/deleted" } function yesDialog { msg_="$1" echo "WARNING!" >&2 echo "========" >&2 echo -n "$msg_" >&2 echo -n " (type uppercase yes): " >&2 read input if [[ $input == 'YES' ]]; then return 0 fi return 1 } function actionFormat { IS_IMAGE_='false' ret_=0 init_='init' if (( $SIZE_ < 3 )); then die "the minimum size of an encrypted luks partition should be 2" fi # TARGET (is/should be) a filesystem image if [ ! -b "$TARGET_" ]; then notice "Operating on a file" IS_IMAGE_='true' if [ -e "$TARGET_" ]; then $FORCE_ || die "$TARGET_ does already exist" warn "overwriting file $TARGET_" init_='donothing' else echo -n "Initialising file with " if [[ $OPTIMIZED_MODE_SET_ == 'true' ]]; then echo "optimised SECURE mode" execute "dd if=/dev/zero of=$TARGET_ bs=1M count=${SIZE_} &>/dev/null" \ die "could not initialise $TARGET_ with /dev/zero" else if [[ $ENTROPY_SOURCE_ == '/dev/zero' ]]; then echo "INSERCURE mode" else echo "SECURE mode (taking /dev/urandom as source, this could take some time)" fi execute "dd if=$ENTROPY_SOURCE_ of=$TARGET_ bs=1M count=${SIZE_} &>/dev/null" ||\ die "could not initialise $TARGET_ with $ENTROPY_SOURCE_" fi fi TARGET_=`findNextFreeLoop` || die "could not find a free loop device" # TARGET_ is now /dev/loop execute "losetup $TARGET_ $ORIG_TARGET_" die if [[ $OPTIMIZED_MODE_SET_ == 'true' || $ENTROPY_SOURCE_ == '/dev/zero' ]]; then execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" \ die "could not initialise the fist 2MB of $TARGET_ with /dev/urandom" fi formatDevice "$init_" ret_=$? else $FORCE_ || (yesDialog "Are you shure you want to overwrite $TARGET_ ?" || die 'You are not sure') notice 'Operating on a device' echo -n 'Initialising device with ' if [[ $OPTIMIZED_MODE_SET_ == 'true' ]]; then echo "optimised SECURE mode" execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" ||\ die "could not initialise the first 2MB of $TARGET_ with /dev/urandom" elif [[ $ENTROPY_SOURCE_ != '/dev/zero' ]]; then # default mode echo "SECURE mode (taking $ENTROPY_SOURCE_ as source, this could take some time)" execute "dd if=$ENTROPY_SOURCE_ of=$TARGET_ bs=1M &>/dev/null" #||\ # skipped because "no space left on device" from dd # die "could not initialise $TARGET_ with $ENTROPY_SOURCE_" else echo 'INSECURE mode (only initialising the fist 2MB with /dev/urandom)' execute "dd if=/dev/urandom of=$TARGET_ bs=1M count=2 &>/dev/null" \ die "could not initialise the first 2MB of $TARGET_ with /dev/urandom" fi formatDevice "$init_" ret_=$? fi # formatDevice was successfully if (( $ret_ == 0 )); then # a mountpoint was given (don't luksClose the device) local mount_point_exists_='true' test -d "$MOUNT_POINT_" || mount_point_exists_='false' if [[ $MOUNT_POINT_ != "" && "$mount_point_exists_" == 'true' ]]; then $READONLY_SET_ && margs_='-r' execute "mount $margs_ $ADDITIONAL_MOUNT_ARGS_ $DM_PATH_ $MOUNT_POINT_" die else if [[ $MOUNT_POINT_ != "" ]]; then $mount_point_exists_ || warn "mountpoint $MOUNT_POINT_ does not exist, not mounting. please use \"grml-crypt start $ORIG_TARGET_ \" to start the device" fi execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn $IS_IMAGE_ && execute "losetup -d $TARGET_" warn fi else execute "$CRYPTSETUP_ luksClose $DM_NAME_" warn $IS_IMAGE_ && execute "losetup -d $TARGET_" warn fi } ### ### __MAIN ### while getopts "s:t:rzoyfFm:hvS:C:I:A:" opt; do case "$opt" in s) SIZE_="$OPTARG"; SIZE_SET_='true' ;; t) FSTYPE_="$OPTARG" ;; r) READONLY_SET_='true' ;; z) let OPTIMIZING_LEVEL_=$OPTIMIZING_LEVEL_+1 ENTROPY_SOURCE_='/dev/zero' warn 'initialising from INSECURE source /dev/zero' ;; o) let OPTIMIZING_LEVEL_=$OPTIMIZING_LEVEL_+1 OPTIMIZED_MODE_SET_='true' ;; y) VERIFY_PW_="--verify-passphrase" ;; f) FORCE_='true' ;; F) FSCK_='true' ;; m) ADDITIONAL_MOUNT_ARGS_="$OPTARG" ;; h) printUsage; exit ;; v) let verbose_=$verbose_+1 ;; S) CIPHER_SIZE_="$OPTARG" ;; C) CIPHER_="$OPTARG" ;; I) ITERATION_TIME_="$OPTARG" ;; A) ADDITIONAL_CRYPTSETUP_ARGS_="$OPTARG" ;; ?) printUsage; exit 64 ;; esac done shift $(($OPTIND - 1)) # set ARGV to the first not parsed commandline parameter setVerbose $verbose_ checkRoot die "You have to be root to use this program" disableSyslog if [[ $1 == 'help' ]]; then printUsage exit 0 fi if (( $# < 2 )); then printUsage die "wrong number of arguments ($#)" 1 fi if (( $OPTIMIZING_LEVEL_ > 1 )); then printUsage die "please choose ONE initialisation methode" fi TARGET_="$2" MKFS_="/sbin/mkfs.$FSTYPE_" if [ ! -x "$MKFS_" ]; then die "invalid filesystem type \"$FSTYPE_\"" 1 fi # use batch-mode if available $CRYPTSETUP_ $BATCH_MODE_ --help &>/dev/null; ret_=$? case "$ret_" in 0) dprint "your cryptsetup understands --batch-mode" ;; 1) BATCH_MODE_=""; notice "your cryptsetup does NOT understand --batch-mode, trying without" ;; 127) die "could not execute cryptsetup" 127 ;; *) warn "problems executing $CRYPTSETUP_" $ret_ esac DM_NAME_="`getDMName $TARGET_`" DM_PATH_="$DEV_MAPPER_/$DM_NAME_" ORIG_TARGET_="$TARGET_" MOUNT_POINT_="$3" case "$1" in format) ACTION_='format'; actionFormat ;; start) ACTION_='start'; actionStart ;; stop) ACTION_='stop'; actionStop "$TARGET_" ;; *) printUsage ;; esac # END OF FILE ################################################################################ # vim:foldmethod=marker tabstop=2 expandtab shiftwidth=2