tomb

the crypto undertaker
git clone git://parazyd.org/tomb.git
Log | Files | Refs | README | LICENSE

commit 110ae83cd1abb38607967662f1bf431578af6281
parent f37f7dd51df9270fdd0f5aeec81f819aabc5c190
Author: Jaromil <jaromil@dyne.org>
Date:   Mon, 27 May 2013 22:49:44 +0200

Relevant improvements to key password handling

now keys are verified in load_key() honoring commanline args
ask_key_password() will challenge user verifying using gnupg
drop_key() should be called after key has been used

this commit removes quite som duplicate code in password handling.

Diffstat:
Mtomb | 333+++++++++++++++++++++++++++++++++++--------------------------------------------
1 file changed, 149 insertions(+), 184 deletions(-)

diff --git a/tomb b/tomb @@ -180,7 +180,7 @@ check_bin() { export PATH=/sbin:/usr/sbin:$PATH # which dd command to use - command -v dcfldd + command -v dcfldd > /dev/null { test $? = 0 } && { DD="dcfldd statusinterval=1" } # which wipe command to use @@ -249,14 +249,14 @@ safe_dir() { if (( $MKTEMP )); then mktemp -d /dev/shm/$1.$$.XXXXXXX else - dir="/dev/shm/$1.$$.$RANDOM$RANDOM" + dir="/dev/shm/$1.$$.$RANDOM$RANDOM" mkdir -m 0700 -p "$dir" print "$dir" fi return 0 else _warning "WARNING: we cannot ensure we're running in RAM." - xxx "Wait a bit before retrying... (attempt $tries)" + xxx "Wait a bit before retrying... (attempt $tries)" sync && sleep 0.5 fi done @@ -827,6 +827,92 @@ dig_tomb() { _message "tomb lock ${tombname}.tomb ${tombname}.tomb.key" } +# this function retrieves a tomb key specified on commandline +# or one implicit if laying nearby the tomb, or from stin +# it also runs checks and creates a temporary key in memory +# to be dropped at the end of functions using it with drop_key() +# on success returns 0 and prints out the full path to the key +typeset -h tombkeydir +load_key() { + # check if the key is set manually then use the one existing + local tombdir="$1" + local tombname="$2" + if option_is_set -k ; then + if [[ "`option_value -k`" == "-" ]]; then + # take key from stdin + local tombkeydir + tombkeydir=`safe_dir` + cat > ${tombkeydir}/stdin.tmp + tombkey=${tombkeydir}/stdin.tmp + else + # take key from a file + tombkey=`option_value -k` + fi + else + # guess key as lying besides the tomb + tombkey=${tombdir}/${tombname}.tomb.key + fi + + if [ -r "${tombkey}" ]; then + _message "We'll use this key:" + _message " `ls -lh ${tombkey}`" + else + return 1 + fi + + # this does a check on the file header, virtuosism by hellekin + # [[ `file =(awk '/^-+BEGIN/,0' $1) -bi` =~ application/pgp ]] + if ! is_valid_key ${tombkey}; then + _warning "The key seems invalid, the application/pgp header is missing" + return 1 + fi + print "$tombkey" + return 0 +} +# it asks the user for the password to use the key +# it tests it against the return code of gpg +# on success returns 0 and prints the password +# (to be saved into a temporary variable!) +ask_key_password() { + tombkey="$1" + local keyname=`basename $tombkey` + _message "a password is required to use key ${keyname}" + local passok=0 + local tombpass="" + if option_is_set --tomb-pwd; then + tombpass=`option_value --tomb-pwd` + else + for c in 1 2 3; do + if [ $c = 1 ]; then + tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname"` + else + tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname (retry $c)"` + fi + if [[ $? != 0 ]]; then + _warning "User aborted password dialog" + return 1 + fi + + get_lukskey "$tombpass" ${tombkey} >/dev/null + + if [ $? = 0 ]; then + passok=1; _message "Password OK." + break; + fi + done + fi + + { test "$passok" = "1" } || { return 1 } + print "$tombpass" + unset $tombpass + return 0 +} +drop_key() { + { test "$tombkeydir" = "" } && { return 0 } + { test -r ${tombkeydir}/stdin.tmp } && { + ${=WIPE} ${tombkeydir}/stdin.tmp; rmdir ${tombkeydir} } +} + # this function locks a tomb with a key file # in fact LUKS formatting the loopback volume # it take arguments as the LUKS cipher to be used @@ -858,7 +944,7 @@ lock_tomb_with_key() { xxx "loop mounted on ${nstloop}" _message "checking if the tomb is empty (we never step on somebody else's bones)" - cryptsetup isLuks ${nstloop} + cryptsetup isLuks ${nstloop} if [ $? = 0 ]; then # is it a LUKS encrypted nest? then bail out and avoid reformatting it _warning "The tomb was already locked with another key" @@ -868,38 +954,12 @@ lock_tomb_with_key() { _message "fine, this tomb seems empty." fi - # check if the key is set manually then use the one existing - if option_is_set -k ; then - if [[ "`option_value -k`" == "-" ]]; then - # take key from stdin - local tombkeydir - tombkeydir=`safe_dir` - cat > ${tombkeydir}/stdin.tmp - tombkey=${tombkeydir}/stdin.tmp - else - # take key from a file - tombkey=`option_value -k` - fi - else - # guess key as lying besides the tomb - tombkey=${tombdir}/${tombname}.tomb.key - fi - - if [ -r "${tombkey}" ]; then - _message "We'll use this key to lock the tomb:" - _message " `ls -lh ${tombkey}`" - else - losetup -d ${nstloop} - die "No key found. Use the option -k to specify a key file." - fi - - # this does a check on the file header, virtuosism by hellekin - # [[ `file =(awk '/^-+BEGIN/,0' $1) -bi` =~ application/pgp ]] - if ! is_valid_key ${tombkey}; then - _warning "The key seems invalid, the application/pgp header is missing" - losetup -d ${nstloop} - die "Operation aborted." - fi + # load key from options or file + tombkey=`load_key ${tombdir} ${tombname}` + { test $? = 0 } || { + losetup -d $nstloop + die "Aborting operations: error loading key $tombkey" } + # make sure to call drop_key later # the encryption cipher for a tomb can be set at creation using -o if option_is_set -o; then @@ -909,35 +969,11 @@ lock_tomb_with_key() { fi _message "locking using cipher: $cipher" - keyname=`basename $tombkey | cut -d. -f1` - _message "a password is required to use key ${keyname}" - local passok=0 - if option_is_set --tomb-pwd; then - tombpass=`option_value --tomb-pwd` - else - for c in 1 2 3; do - if [ $c = 1 ]; then - tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname"` - else - tombpass=`exec_as_user ${TOMBEXEC} askpass "Insert password to use key: $keyname (retry $c)"` - fi - if [[ $? != 0 ]]; then - losetup -d ${nstloop} - die "User aborted" - fi - - get_lukskey "${tombpass}" ${tombkey} >/dev/null - if [ $? = 0 ]; then - passok=1; _message "Password OK." - break; - fi - done - fi - if [ "$passok" = "0" ]; then - _warning "Password incorrect" - losetup -d $nstloop - die "Operation aborted." - fi + # get the pass from the user and check it + tombpass=`ask_key_password "$tombkey"` + { test $? = 0 } || { + losetup -d ${nstloop} + die "No valid password supplied" } _success "Locking ${tombfile} with ${tombkey}" @@ -953,8 +989,6 @@ lock_tomb_with_key() { die "Operation aborted." fi - - get_lukskey "${tombpass}" ${tombkey} | \ cryptsetup --key-file - \ --cipher ${cipher} luksOpen ${nstloop} tomb.tmp @@ -965,7 +999,9 @@ lock_tomb_with_key() { die "Operation aborted." fi + # cleanup tombs unset tombpass + drop_key # make sure all temp files are out _message "formatting your Tomb with Ext3/Ext4 filesystem" ${=MKFS} ${tombname} /dev/mapper/tomb.tmp @@ -1024,7 +1060,7 @@ is_valid_key() { #will output the lukskey get_lukskey() { local tombpass=$1 - keyfile=$2 + local keyfile=$2 firstline=`head -n1 $keyfile` xxx "get_lukskey XXX $keyfile" if [[ $firstline =~ '^_KDF_' ]]; then @@ -1164,33 +1200,14 @@ mount_tomb() { tombname=${tombfile%%\.*} xxx "tomb found: ${tombdir}/${tombfile}" - if option_is_set -k ; then - if [[ "`option_value -k`" == "-" ]]; then - # take key from stdin - local tombkeydir - tombkeydir=`safe_dir` - cat > ${tombkeydir}/stdin.tmp - tombkey=${tombkeydir}/stdin.tmp - else - # take key from a file - tombkey=`option_value -k` - fi - else - # guess key as lying besides the tomb - tombkey=${tombdir}/${tombfile}.key - fi - if ! [ -r ${tombkey} ]; then - _warning "key file not found: ${tombkey}" - _warning "operation aborted." - return 1 - fi + tombkey=`load_key ${tombdir} ${tombname}` + { test $? = 0 } || { + die "Aborting operations: error loading key $tombkey" } - if ! [ $2 ]; then + + if [ "$2" = "" ]; then tombmount=/media/${tombfile} _message "mountpoint not specified, using default: $tombmount" - elif ! [ -x $2 ]; then - _warning "mountpoint $2 doesn't exist, operation aborted." - return 1 else tombmount=$2 fi @@ -1198,7 +1215,7 @@ mount_tomb() { # check if its already open mount -l | grep "${tombfile}.*\[$tombname\]$" 2>&1 > /dev/null if [ $? = 0 ]; then - _warning "$tombname is already open on $tombmount" + _warning "$tombname is already open." _message "here below its status is reported:" list_tombs ${tombname} return 1 @@ -1206,8 +1223,6 @@ mount_tomb() { _success "Opening $tombfile on $tombmount" - # we need root from here on - mkdir -p $tombmount nstloop=`losetup -f` if [ $? = 255 ]; then @@ -1219,7 +1234,6 @@ mount_tomb() { if [ $? != 0 ]; then # is it a LUKS encrypted nest? see cryptsetup(1) _warning "$tombfile is not a valid Luks encrypted storage file" - $norm || rmdir $tombmount 2>/dev/null return 1 fi say "this tomb is a valid LUKS encrypted device" @@ -1241,45 +1255,21 @@ mount_tomb() { # save date of mount in minutes since 1970 mapdate=`date +%s` - mapper="tomb.${tombname}.${mapdate}.`basename $nstloop`" keyname=`basename $tombkey | cut -d. -f1` - _warning "Password is required for key ${keyname}" - for c in 1 2 3; do - if ! option_is_set --tomb-pwd; then - tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb ${keyname}"` - if [[ $? != 0 ]]; then - die "User aborted" - fi - else - tombpass=`option_value --tomb-pwd` - fi - get_lukskey "${tombpass}" ${tombkey} | \ - cryptsetup --key-file - luksOpen ${nstloop} ${mapper} - local ret=$? - unset tombpass - if [[ $ret != 0 ]]; then - if [[ $c = 3 ]] || option_is_set --tomb-pwd; then - die "Wrong password: aborting" - fi - continue - fi + tombpass=`ask_key_password $tombkey` + { test $? = 0 } || { + die "No valid password supplied" } - # if key was from stdin delete temp file and dir - if [ $tombkeydir ]; then - ${=WIPE} ${tombkey} - rmdir $tombkeydir - fi + get_lukskey "${tombpass}" ${tombkey} | \ + cryptsetup --key-file - luksOpen ${nstloop} ${mapper} - if [ -r /dev/mapper/${mapper} ]; then - break; # password was correct - fi - done + drop_key + unset tombpass if ! [ -r /dev/mapper/${mapper} ]; then losetup -d ${nstloop} - $norm || rmdir ${tombmount} 2>/dev/null die "failure mounting the encrypted file" fi @@ -1296,6 +1286,9 @@ mount_tomb() { xxx "tomb engraved as $tombname" tune2fs -L ${tombname} /dev/mapper/${mapper} > /dev/null + # we need root from here on + mkdir -p $tombmount + mount -o $MOUNTOPTS /dev/mapper/${mapper} ${tombmount} chown ${_uid}:${_gid} ${tombmount} @@ -1364,7 +1357,7 @@ BEGIN { main="" } } ' else - # list a specific tomb + # list a specific tomb mount -l \ | awk -vtomb="[$1]" ' BEGIN { main="" } @@ -1534,9 +1527,9 @@ umount_tomb() { # # kill the status tray widget if still present # # this makes the widget disappear when closing tomb from cli - # awkmapper=`sed 's:\/:\\\/:g' <<< $mapper` - # statustray_pid=`ps ax | awk "/tomb-status $awkmapper/"' {print $1} '` - # { test "$statustray_pid" = "" } || { kill ${statustray_pid} } + # awkmapper=`sed 's:\/:\\\/:g' <<< $mapper` + # statustray_pid=`ps ax | awk "/tomb-status $awkmapper/"' {print $1} '` + # { test "$statustray_pid" = "" } || { kill ${statustray_pid} } _success "Tomb $tombname closed: your bones will rest in peace." @@ -1574,23 +1567,14 @@ change_passwd() { lukskey=`safe_filename tombluks` _success "Changing password for $keyfile" - keyname=`basename $keyfile` - if ! option_is_set --tomb-old-pwd; then - while true; do - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type old password for ${keyname}" "Change tomb key password"` - if [[ $? == 1 ]]; then - die "User aborted" - fi - if get_lukskey "${tombpass}" ${keyfile} > ${lukskey}; then - break - fi - done - else - tombpass=`option_value --tomb-old-pwd` - if ! get_lukskey "${tombpass}" ${keyfile} > ${lukskey}; then - die "Invalid old password" - fi - fi + + tombpass=`ask_key_password $keyfile` + { test $? = 0 } || { + die "No valid password supplied" } + + get_lukskey "${tombpass}" ${keyfile} > ${lukskey}; + + drop_key { gen_key $lukskey > ${tmpnewkey} @@ -1631,25 +1615,11 @@ resize_tomb() { local tombname=${tombfile%%\.*} tombfile=${tombname}.tomb - if option_is_set -k ; then - if [[ "`option_value -k`" == "-" ]]; then - # take key from stdin - local tombkeydir - tombkeydir=`safe_dir` - cat > ${tombkeydir}/stdin.tmp - tombkey=${tombkeydir}/stdin.tmp - else - # take key from a file - tombkey=`option_value -k` - fi - else - # guess key as lying besides the tomb - tombkey=${tombdir}/${tombfile}.key - fi - - if ! [ -r ${tombkey} ]; then - _failure "key file not found: ${tombkey}" - fi + # load key from options or file + tombkey=`load_key ${tombdir} ${tombname}` + { test $? = 0 } || { + die "Aborting operations: error loading key $tombkey" } + # make sure to call drop_key later local tmp_resize=`safe_filename tmbrsz` local newtombsize=$opts[-s] @@ -1688,31 +1658,26 @@ resize_tomb() { cat "${tmp_resize}" >> "$1" ${=WIPE} "${tmp_resize}" + + tombpass=`ask_key_password $tombkey` + { test $? = 0 } || { + die "No valid password supplied" } + local nstloop=`losetup -f` if [ $? = 255 ]; then _failure "too many tomb opened. Please close any of them to open another tomb" fi + losetup -f "$1" local mapdate=`date +%s` local mapper="tomb.${tombname}.${mapdate}.`basename $nstloop`" - _message "Password is required for key ${keyname}" - for c in 1 2 3; do - if [ $c = 1 ]; then - tombpass=`exec_as_user ${TOMBEXEC} askpass ${keyname}` - else - tombpass=`exec_as_user ${TOMBEXEC} askpass "$keyname (retry $c)"` - fi - get_lukskey "${tombpass}" ${tombkey} | \ - cryptsetup --key-file - luksOpen ${nstloop} ${mapper} - - unset tombpass + get_lukskey "${tombpass}" ${tombkey} | \ + cryptsetup --key-file - luksOpen ${nstloop} ${mapper} - if [ -r /dev/mapper/${mapper} ]; then - break; # password was correct - fi - done + unset tombpass + drop_key # cleanup after load_key if ! [ -r /dev/mapper/${mapper} ]; then losetup -d ${nstloop} @@ -1974,7 +1939,7 @@ main() { subcommands_opts[create]="f s: -size=s -force k: -key=k -ignore-swap -kdf: -sudo-pwd: -tomb-pwd: -use-urandom" - subcommands_opts[forge]="f -ignore-swap -kdf: -use-urandom" + subcommands_opts[forge]="f -ignore-swap k: -key=k -kdf: -use-urandom" subcommands_opts[dig]="f -ignore-swap s: -size=s" subcommands_opts[lock]="f -force -ignore-swap s: -size=s k: -key=k -sudo-pwd: -tomb-pwd:" @@ -2146,7 +2111,7 @@ main() { encode_key $PARAM[1] $PARAM[2] ;; exhume) if [ "$STEGHIDE" = 0 ]; then - _warning "steghide not installed. Cannot exhume your key" + _warning "steghide not installed. Cannot exhume your key" return 1 fi decode_key $PARAM[1] $PARAM[2]