improvements to key handling

added a new 'change' command to change a Tomb's key
it replaces the same LUKS slot using luksChangeKey
This commit is contained in:
Jaromil 2013-06-19 20:20:17 +02:00
parent 9706ef1ab1
commit 8f4b0c6567

189
tomb
View File

@ -222,6 +222,30 @@ EOF
return 0 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 # {{{ Commandline interaction
@ -438,16 +462,20 @@ check_bin() {
# {{{ Key operations # {{{ Key operations
# This function retrieves a tomb key specified on commandline or one # This function retrieves a tomb key specified on commandline or one
# laying nearby the tomb if found, or from stdin if the option was # laying nearby the tomb if found, or from stdin if the option was
# selected. It also runs validity checks on the file. Callers should # selected. It also runs validity checks on the file. Callers should
# always use drop_key() when done with all key operations. # always use drop_key() when done with all key operations.
# On success returns 0 and prints out the full path to the key # On success returns 0 and prints out the full path to the key
load_key() { load_key() {
# take the name of a tomb file as argument
# this is used for guessing if the key is nearby
{ test "$1" = "" } || {
tombdir=`dirname $1` tombdir=`dirname $1`
tombfile=`basename $1` tombfile=`basename $1`
tombname=${tombfile%%\.*} tombname=${tombfile%%\.*}
}
if option_is_set -k ; then if option_is_set -k ; then
if [[ "`option_value -k`" == "-" ]]; then if [[ "`option_value -k`" == "-" ]]; then
xxx "load_key reading from stdin" xxx "load_key reading from stdin"
@ -594,8 +622,18 @@ drop_key() {
#$1 is the keyfile we are checking #$1 is the keyfile we are checking
is_valid_key() { 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 # 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 # if no BEGIN header found then we try to recover it
[[ `file $1 -bi` =~ text/plain ]] && { [[ `file $1 -bi` =~ text/plain ]] && {
_warning "Key data found with missing headers, attempting recovery" _warning "Key data found with missing headers, attempting recovery"
@ -658,7 +696,7 @@ get_lukskey() {
ret=$? ret=$?
unset tombpass unset tombpass
else # using status-file in gpg != 1.4.12 else # using status-file in gpg != 1.4.11
res=`safe_filename lukskey` res=`safe_filename lukskey`
{ test $? = 0 } || { unset tombpass; die "Fatal error creating temp file." } { test $? = 0 } || { unset tombpass; die "Fatal error creating temp file." }
@ -667,6 +705,7 @@ get_lukskey() {
gpg --batch --passphrase-fd 0 --no-tty --no-options --status-fd 2 \ gpg --batch --passphrase-fd 0 --no-tty --no-options --status-fd 2 \
--no-mdc-warning --no-permission-warning --no-secmem-warning \ --no-mdc-warning --no-permission-warning --no-secmem-warning \
-d "${keyfile}" 2> $res -d "${keyfile}" 2> $res
unset tombpass unset tombpass
grep 'DECRYPTION_OKAY' $res grep 'DECRYPTION_OKAY' $res
ret=$?; rm -f $res ret=$?; rm -f $res
@ -681,7 +720,9 @@ get_lukskey() {
# honored options: --kdf --tomb-pwd # honored options: --kdf --tomb-pwd
gen_key() { gen_key() {
# $1 the lukskey to encrypt # $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 # here user is prompted for key password
local tombpass="" local tombpass=""
local tombpasstmp="" local tombpasstmp=""
@ -730,10 +771,12 @@ gen_key() {
header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n" header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
} }
print -n $header print -n $header
print "${tombpass}" \ print "${tombpass}" \
| gpg --openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \ | gpg --openpgp --force-mdc --cipher-algo ${algo} \
--batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
-o - -c -a ${lukskey} -o - -c -a ${lukskey}
unset tombpass unset tombpass
@ -855,22 +898,24 @@ exhume_key() {
forge_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" _warning "no key name specified for creation"
return 1 } return 1 }
{ test -r "$1" } && { { test -r "$destkey" } && {
_warning "Forging this key would overwrite an existing file. Operation aborted." _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 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 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 # 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" (( $? )) && die "error creating temp dir"
xxx "safe_dir at $keytmp" xxx "safe_dir at $keytmp"
@ -881,7 +926,13 @@ forge_key() {
die "operation aborted." die "operation aborted."
fi 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 "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." _message "once done you will be asked to choose a password for your tomb."
@ -890,7 +941,7 @@ forge_key() {
touch ${keytmp}/tomb.tmp touch ${keytmp}/tomb.tmp
chmod 0600 ${keytmp}/tomb.tmp chmod 0600 ${keytmp}/tomb.tmp
random_source=/dev/random local random_source=/dev/random
if option_is_set --use-urandom; then if option_is_set --use-urandom; then
random_source=/dev/urandom random_source=/dev/urandom
fi fi
@ -913,8 +964,7 @@ forge_key() {
tombname="$tombkey" tombname="$tombkey"
# the gen_key() function takes care of the new key's encryption # 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 # this does a check on the file header
if ! is_valid_key ${tombkey}; then if ! is_valid_key ${tombkey}; then
_warning "The key does not seem to be valid" _warning "The key does not seem to be valid"
@ -1044,7 +1094,7 @@ lock_tomb_with_key() {
die "Aborting operations: error loading key $tombkey" } die "Aborting operations: error loading key $tombkey" }
# make sure to call drop_key later # 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 if option_is_set -o; then
cipher="`option_value -o`" cipher="`option_value -o`"
else else
@ -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 # backward compatibility
create_tomb() { create_tomb() {
xxx "create_tomb(): ${=@} ${=OLDARGS}" xxx "create_tomb(): ${=@} ${=OLDARGS}"
@ -1922,14 +2056,15 @@ main() {
# -force and NOT -f # -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) 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[__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[mount]=${subcommands_opts[open]}
subcommands_opts[create]="" # deprecated, will issue warning subcommands_opts[create]="" # deprecated, will issue warning
subcommands_opts[forge]="f -force -ignore-swap k: -key=k -kdf: -tomb-pwd: -use-urandom" 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[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[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[passwd]="f -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: "
subcommands_opts[close]="-sudo-pwd: " subcommands_opts[close]="-sudo-pwd: "
@ -1943,9 +2078,9 @@ main() {
subcommands_opts[help]="" subcommands_opts[help]=""
subcommands_opts[bury]="f -force k: -key=k -tomb-pwd: " subcommands_opts[bury]="f -force k: -key=k -tomb-pwd: "
subcommands_opts[exhume]="f -force k: -key=k -tomb-pwd: " subcommands_opts[exhume]="f -force k: -key=k -tomb-pwd: "
subcommands_opts[decompose]="" # subcommands_opts[decompose]=""
subcommands_opts[recompose]="" # subcommands_opts[recompose]=""
subcommands_opts[install]="" # subcommands_opts[install]=""
subcommands_opts[askpass]="" subcommands_opts[askpass]=""
subcommands_opts[mktemp]="" subcommands_opts[mktemp]=""
subcommands_opts[source]="" subcommands_opts[source]=""
@ -1973,7 +2108,7 @@ main() {
fi fi
unset discardme unset discardme
if ! zparseopts -M -E -D -Adiscardme ${every_opts}; then if ! zparseopts -M -E -D -Adiscardme ${every_opts}; then
error "error parsing" die "error parsing"
return 127 return 127
fi fi
unset discardme unset discardme
@ -2055,6 +2190,12 @@ main() {
check_priv check_priv
lock_tomb_with_key ${=PARAM} lock_tomb_with_key ${=PARAM}
;; ;;
change)
check_priv
change_tomb_key ${=PARAM}
;;
# backward compat # backward compat
create) create)
_warning "The create command is deprecated, please use dig, forge and lock instead." _warning "The create command is deprecated, please use dig, forge and lock instead."
@ -2122,7 +2263,7 @@ EOF
option_is_set -v && { option_is_set -v && {
cat <<EOF cat <<EOF
GnuPG available ciphers: Key forging algorithms (GnuPG symmetric ciphers):
`list_gnupg_ciphers` `list_gnupg_ciphers`
EOF EOF
return 0 return 0