timprovements to key handling - tomb - the crypto undertaker
 (HTM) git clone git://parazyd.org/tomb.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 8f4b0c65675135519391bc6eea127d039359639c
 (DIR) parent 9706ef1ab142aad95b7838c5c101ca2276410cb3
 (HTM) Author: Jaromil <jaromil@dyne.org>
       Date:   Wed, 19 Jun 2013 20:20:17 +0200
       
       improvements to key handling
       
       added a new 'change' command to change a Tomb's key
       it replaces the same LUKS slot using luksChangeKey
       
       Diffstat:
         M tomb                                |     213 +++++++++++++++++++++++++------
       
       1 file changed, 177 insertions(+), 36 deletions(-)
       ---
 (DIR) diff --git a/tomb b/tomb
       t@@ -222,6 +222,30 @@ EOF
            return 0
        }
        
       +# check if a filename is a valid tomb
       +is_valid_tomb() {
       +    xxx "is_valid_tomb $1"
       +    # argument check
       +    { test "$1" = "" } && {
       +        _warning "Tomb file is missing from arguments"; return 1 }
       +    # file checks
       +    { test -r "$1" } || {
       +        _warning "Tomb file not found: $1"; return 1 }
       +    { test -f "$1" } || {
       +        _warning "Tomb file is not a regular file: $1"; return 1 }
       +    # check file type (if its a Luks fs)
       +    file "$1" | grep -i 'luks encrypted file' >/dev/null
       +    { test $? = 0 } || {
       +        _warning "File is not a valid tomb: $1"; return 1 }
       +    # check if its already open
       +    tombfile=`basename $1`
       +    tombname=${tombfile%%\.*}
       +    mount -l | grep "${tombfile}.*\[$tombname\]$" > /dev/null
       +    { test $? = 0 } && {
       +        _warning "Tomb is currently in use: $tombname"; return 1 }
       +    _message "Valid tomb file found: $1"
       +    return 0
       +}
        # }}}
        
        # {{{ Commandline interaction
       t@@ -438,16 +462,20 @@ check_bin() {
        
        # {{{ Key operations
        
       -
        # This function retrieves a tomb key specified on commandline or one
        # laying nearby the tomb if found, or from stdin if the option was
        # selected.  It also runs validity checks on the file. Callers should
        # always use drop_key() when done with all key operations.
        # On success returns 0 and prints out the full path to the key
        load_key() {
       -    tombdir=`dirname $1`
       -    tombfile=`basename $1`
       -    tombname=${tombfile%%\.*}
       +    # take the name of a tomb file as argument
       +    # this is used for guessing if the key is nearby
       +    { test "$1" = "" } || {
       +        tombdir=`dirname $1`
       +        tombfile=`basename $1`
       +        tombname=${tombfile%%\.*}
       +    }
       +
            if option_is_set -k ; then
                if [[ "`option_value -k`" == "-" ]]; then
                    xxx "load_key reading from stdin"
       t@@ -594,8 +622,18 @@ drop_key() {
        
        #$1 is the keyfile we are checking
        is_valid_key() {
       +    xxx "is_valid_key $1"
       +    # argument check
       +    { test "$1" = "" } && {
       +        _warning "Key file is missing from arguments"; return 1 }
       +    # file checks
       +    { test -r "$1" } || {
       +        _warning "Key file not found: $1"; return 1 }
       +    { test -f "$1" } || {
       +        _warning "Key file is not a regular file: $1"; return 1 }
            # this header validity check is a virtuosism by Hellekin
       -    [[ `file =(awk '/^-+BEGIN/,0' $1)` =~ PGP ]] && return 0
       +    [[ `file =(awk '/^-+BEGIN/,0' $1)` =~ PGP ]] && {
       +        _message "Valid key file found: $1"; return 0 }
            # if no BEGIN header found then we try to recover it
            [[ `file $1 -bi` =~ text/plain ]] && {
                _warning "Key data found with missing headers, attempting recovery"
       t@@ -658,7 +696,7 @@ get_lukskey() {
                ret=$?
                unset tombpass
        
       -    else # using status-file in gpg != 1.4.12
       +    else # using status-file in gpg != 1.4.11
        
                res=`safe_filename lukskey`
                { test $? = 0 } || { unset tombpass; die "Fatal error creating temp file." }
       t@@ -666,7 +704,8 @@ get_lukskey() {
                print ${tombpass} | \
                    gpg --batch --passphrase-fd 0 --no-tty --no-options --status-fd 2 \
                    --no-mdc-warning --no-permission-warning --no-secmem-warning \
       -            -d "${keyfile}" 2>$res
       +            -d "${keyfile}" 2> $res
       +
                unset tombpass
                grep 'DECRYPTION_OKAY' $res
                ret=$?; rm -f $res
       t@@ -681,7 +720,9 @@ get_lukskey() {
        # honored options: --kdf  --tomb-pwd
        gen_key() {
        # $1 the lukskey to encrypt
       -    local lukskey=$1
       +# $2 is the --cipher-algo to use (string taken by GnuPG)
       +    local lukskey="$1"
       +    local algo="$2"
            # here user is prompted for key password
            local tombpass=""
            local tombpasstmp=""
       t@@ -730,11 +771,13 @@ gen_key() {
                header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
            }
        
       +
            print -n $header
        
            print "${tombpass}" \
       -        | gpg --openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \
       -        -o - -c -a ${lukskey}
       +        | gpg --openpgp --force-mdc --cipher-algo ${algo} \
       +        --batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
       +             -o - -c -a ${lukskey}
        
            unset tombpass
        }
       t@@ -855,22 +898,24 @@ exhume_key() {
        
        
        forge_key() {
       -    _message "Commanded to forge key $1"
       +    xxx "forge_key()"
       +    # can be specified both as simple argument or using -k
       +    local destkey="$1"
       +    { option_is_set -k } && { destkey="`option_value -k`" }
        
       -    { test "$1" = "" } && {
       +    { test "$destkey" = "" } && {
                _warning "no key name specified for creation"
                return 1 }
        
       -    { test -r "$1" } && {
       +    { test -r "$destkey" } && {
                _warning "Forging this key would overwrite an existing file. Operation aborted."
       -        die "`ls -lh $1`"    }
       +        die "`ls -lh $destkey`"    }
        
            # if swap is on, we remind the user about possible data leaks to disk
            if ! option_is_set -f && ! option_is_set --ignore-swap; then check_swap; fi
        
       -
            # create the keyfile in tmpfs so that we leave less traces in RAM
       -    keytmp=`safe_dir forge`
       +    local keytmp=`safe_dir forge`
            (( $? )) && die "error creating temp dir"
            xxx "safe_dir at $keytmp"
        
       t@@ -881,7 +926,13 @@ forge_key() {
                die "operation aborted."
            fi
        
       -    tombkey="$1"
       +    local algo
       +    { option_is_set -o } && { algopt="`option_value -o`" }
       +    algo=${algopt:-AES256}
       +
       +    _message "Commanded to forge key $destkey with cipher algorithm $algo"
       +
       +    local tombkey="$destkey"
        
            _message "this operation takes time, keep using this computer on other tasks,"
            _message "once done you will be asked to choose a password for your tomb."
       t@@ -890,7 +941,7 @@ forge_key() {
        
            touch ${keytmp}/tomb.tmp
            chmod 0600 ${keytmp}/tomb.tmp
       -    random_source=/dev/random
       +    local random_source=/dev/random
            if option_is_set --use-urandom; then
                random_source=/dev/urandom
            fi
       t@@ -913,8 +964,7 @@ forge_key() {
        
            tombname="$tombkey"
            # the gen_key() function takes care of the new key's encryption
       -    gen_key ${keytmp}/tomb.tmp > ${tombkey}
       -
       +    gen_key "${keytmp}/tomb.tmp" "$algo" > ${tombkey}
            # this does a check on the file header
            if ! is_valid_key ${tombkey}; then
                _warning "The key does not seem to be valid"
       t@@ -1044,7 +1094,7 @@ lock_tomb_with_key() {
                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
       +    # the encryption cipher for a tomb can be set when locking using -o
            if option_is_set -o; then
                cipher="`option_value -o`"
            else
       t@@ -1105,6 +1155,90 @@ lock_tomb_with_key() {
        
        }
        
       +# This function changes the key that locks a tomb
       +change_tomb_key() {
       +    if ! option_is_set -f && ! option_is_set --ignore-swap; then check_swap; fi
       +
       +    { option_is_set -k } || { die "Specify the new key with -k" }
       +    newkey="`option_value -k`"
       +
       +    { is_valid_key "$newkey" } || {
       +        die "New key invalid. Check your usage of the --key option." }
       +
       +    oldkey="$1"
       +    { is_valid_key "$oldkey" } || {
       +        die "Old key invalid. Check your usage of the first argument." }
       +
       +    { is_valid_tomb "$2" } || {
       +        die "Specify the name of a tomb as second argument" }
       +
       +    nstloop=`losetup -f`
       +    { test $? = 255 } && {
       +        die "Too many tombs are open. Please close any of them to proceed." }
       +    losetup -f "$2"
       +    cryptsetup isLuks ${nstloop}
       +    # is it a LUKS encrypted nest? we check one more timesee cryptsetup(1)
       +    { test $? = 0 } || { 
       +        losetup -d "$nstloop"
       +        die "Not a valid LUKS encrypted volume: $2" }
       +
       +    # we have everything, prepare to mount
       +    yes "Changing lock on tomb $tombname"
       +    _message "old key: $oldkey"
       +    _message "new key: $newkey"
       +
       +    # render the mapper
       +    mapdate=`date +%s`
       +    # save date of mount in minutes since 1970
       +    mapper="tomb.${tombname}.${mapdate}.`basename $nstloop`"
       +
       +    
       +    # load the new key from the -k option
       +    tombkey=`load_key`
       +    { test $? = 0 } || {
       +        die "Aborting operations: error loading new key $tombkey"    }
       +    
       +    newkeypass=`ask_key_password $tombkey`
       +    { test $? = 0 } || {
       +        die "No valid password supplied for the new key" }
       +    newkeyfile="`safe_filename newkey`"
       +    get_lukskey "$newkeypass" "$newkey" > $newkeyfile
       +
       +    # load the old key
       +    oldkeypass="`ask_key_password $oldkey`"
       +    { test $? = 0 } || {
       +        die "No valid password supplied for the old key" }
       +
       +    # luksOpen the tomb (not really mounting, just on the loopback)
       +    get_lukskey "$oldkeypass" "$oldkey" | \
       +        cryptsetup --key-file - luksOpen ${nstloop} ${mapper}
       +    { test $? = 0 } || {
       +        losetup -d "$nstloop"
       +        die "Unexpected error in luksOpen." }
       +
       +    get_lukskey "$oldkeypass" "$oldkey" | \
       +        cryptsetup --key-file - luksChangeKey "$nstloop" "$newkeyfile"
       +    { test $? = 0 } || {
       +        losetup -d "$nstloop"
       +        die "Unexpected error in luksChangeKey." }
       +
       +    cryptsetup luksClose "${mapper}"
       +    { test $? = 0 } || {
       +        losetup -d "$nstloop"
       +        die "Unexpected error in luksClose." }
       +
       +
       +    drop_key
       +    unset tombpass
       +    ${=WIPE} "$newkeyfile"
       +    losetup -d ${nstloop}
       +    
       +    yes "Succesfully changed key for tomb: $2"
       +    _message "The new key is: $newkey"
       +
       +    return 0
       +}
       +
        # backward compatibility
        create_tomb() {
            xxx "create_tomb(): ${=@} ${=OLDARGS}"
       t@@ -1922,35 +2056,36 @@ main() {
            #    -force and NOT -f
            main_opts=(q -quiet=q D -debug=D h -help=h v -version=v U: -uid=U G: -gid=G T: -tty=T -no-color -unsecure-dev-mode)
            subcommands_opts[__default]=""
       -    subcommands_opts[open]="f n -nohook=n k: -key=k o: -mount-options=o -ignore-swap -sudo-pwd: -tomb-pwd:"
       +    subcommands_opts[open]="f -force n -nohook=n k: -key=k o: -ignore-swap -sudo-pwd: -tomb-pwd: "
            subcommands_opts[mount]=${subcommands_opts[open]}
        
            subcommands_opts[create]="" # deprecated, will issue warning
        
       -    subcommands_opts[forge]="f -force -ignore-swap k: -key=k -kdf: -tomb-pwd: -use-urandom"
       -    subcommands_opts[dig]="f -force -ignore-swap s: -size=s"
       -    subcommands_opts[lock]="f -force -ignore-swap s: -size=s k: -key=k -sudo-pwd: -tomb-pwd:"
       +    subcommands_opts[forge]="f -force -ignore-swap k: -key=k -kdf: o: -tomb-pwd: -use-urandom "
       +    subcommands_opts[dig]="f -force -ignore-swap s: -size=s "
       +    subcommands_opts[lock]="f -force -ignore-swap k: -key=k o: -sudo-pwd: -tomb-pwd: "
       +    subcommands_opts[change]="f -force -ignore-swap k: -key=k -sudo-pwd: -tomb-pwd: "
        
            subcommands_opts[passwd]="f -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: "
       -    subcommands_opts[close]="-sudo-pwd:"
       +    subcommands_opts[close]="-sudo-pwd: "
            subcommands_opts[help]=""
            subcommands_opts[slam]=""
       -    subcommands_opts[list]="-get-mountpoint"
       +    subcommands_opts[list]="-get-mountpoint "
        
            subcommands_opts[index]=""
            subcommands_opts[search]=""
        
            subcommands_opts[help]=""
       -    subcommands_opts[bury]="f -force k: -key=k -tomb-pwd:"
       -    subcommands_opts[exhume]="f -force k: -key=k -tomb-pwd:"
       -    subcommands_opts[decompose]=""
       -    subcommands_opts[recompose]=""
       -    subcommands_opts[install]=""
       +    subcommands_opts[bury]="f -force k: -key=k -tomb-pwd: "
       +    subcommands_opts[exhume]="f -force k: -key=k -tomb-pwd: "
       +    # subcommands_opts[decompose]=""
       +    # subcommands_opts[recompose]=""
       +    # subcommands_opts[install]=""
            subcommands_opts[askpass]=""
            subcommands_opts[mktemp]=""
            subcommands_opts[source]=""
       -    subcommands_opts[resize]="f -force -ignore-swap s: -size=s k: -key=k -tomb-pwd:"
       -    subcommands_opts[check]="-ignore-swap"
       +    subcommands_opts[resize]="f -force -ignore-swap s: -size=s k: -key=k -tomb-pwd: "
       +    subcommands_opts[check]="-ignore-swap "
        #    subcommands_opts[translate]=""
        
            ### Detect subcommand
       t@@ -1973,7 +2108,7 @@ main() {
                fi
                unset discardme
            if ! zparseopts -M -E -D -Adiscardme ${every_opts}; then
       -                error "error parsing"
       +                die "error parsing"
                        return 127
                fi
            unset discardme
       t@@ -2055,6 +2190,12 @@ main() {
                    check_priv
                    lock_tomb_with_key ${=PARAM}
                    ;;
       +
       +        change)
       +            check_priv
       +            change_tomb_key ${=PARAM}
       +            ;;
       +
                # backward compat
                create)
                    _warning "The create command is deprecated, please use dig, forge and lock instead."
       t@@ -2122,7 +2263,7 @@ EOF
                    option_is_set -v && {
                        cat <<EOF
        
       -GnuPG available ciphers:
       +Key forging algorithms (GnuPG symmetric ciphers):
        `list_gnupg_ciphers`
        EOF
                        return 0