tomb

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

commit 28d81f10049dd3df3ceb1c4184edcde0ae64fb39
parent 02787f4418fe4ab0987f201dd5e713cd4269b514
Author: Hellekin O. Wolf <hellekin@cepheide.org>
Date:   Thu,  1 Dec 2011 13:52:47 -0800

Merge pull request #73 from hellekin/cleanup-square

Merge cleanup refactoring
Diffstat:
MHACKING | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mconfigure.ac | 2+-
Mshare/Makefile.am | 7+++----
Msrc/tomb | 1340++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/undertaker | 39++++++++++++++++++---------------------
5 files changed, 790 insertions(+), 653 deletions(-)

diff --git a/HACKING b/HACKING @@ -19,3 +19,58 @@ If you feel the need to name a variable `$longdescriptionofwhatthisisusefulfor`, then you're allowed to use underscores. But do you really need? functions should be lowercase+underscores: `do_this()` + + +Reporting to the user +--------------------- + +There are some nifty messaging functions to use. They all come with +shortcuts that you can use during development or for temporary +messages. The long name is to be used for translatable strings. + +They display formatted messages, using colors when available. + +DEBUG=1 make the _verbose messages visible. +QUIET=1 suppresses all messages (but the _verbose messages if DEBUG=1). + +Here is the deal: + +Name (Shortcut) Return When to use +================================================================================= +_failure (die) exit 1 You want to exit the program with a fatal error. + You may pass a different exit code as second argument. + +_warning (no) You want to inform the user about an error condition. + +_message (say) You want to tell the user about what's going on. + You can pass -n (shortcut: act) for inline messages. + +_verbose (xxx) You need to check the current state of the program. + +_success (yes) You want to tell the user about a successful result. + +All messaging function take a single "message" argument. +_failure takes an exit code as an optional second argument. + +Examples: + + _verbose "Showing translatable debug message" + xxx "This is temporary debug" + _message "The program is doing something" + _message -n "Inline messages " + echo "are useful" + _success "All is fine" + _warning "Something's wrong" + _failure "Fatal error: exiting with default exit code 1" + _message "This is not reached, nor the next 'die' command" + die "I am Jack's dead corpse." 127 + +Will display something like: + + tomb [D] Showing translatable debug message + tomb [D] This is temporary debug + tomb . The program is doing something + tomb > Inline messages are useful + tomb (*) All is fine + tomb [W] Something's wrong + tomb [E] Fatal error: exiting with default exit code 1 diff --git a/configure.ac b/configure.ac @@ -79,7 +79,7 @@ dnl these are not *build* dependencies, but *runtime* dependencies. dnl Mandatory AC_CHECK_PROG(have_zsh,zsh,yes,no) -AC_CHECK_PROG(have_cryptsetup,cryptsetup,yes,no) +AC_CHECK_PROG(have_cryptsetup,cryptsetup,yes,no,[/sbin$PATH_SEPARATOR/usr/local/sbin$PATH_SEPARATOR$PATH]) AC_CHECK_PROG(have_pinentry,pinentry,yes,no) AC_CHECK_PROG(have_sudo,sudo,yes,no) dnl Optional diff --git a/share/Makefile.am b/share/Makefile.am @@ -46,13 +46,13 @@ install-data-hook: $(top_srcdir)/src/monmort.xpm monmort; \ xdg-icon-resource install --size 32 $(top_srcdir)/src/monmort.xpm monmort; fi @mkdir -p $(DESTDIR)/$(datadir)/icons/hicolor/32x32/mimetypes; \ - ln -s $(AppInstIcondir)/monmort.png \ + ln -sf $(AppInstIcondir)/monmort.png \ $(DESTDIR)/$(datadir)/icons/hicolor/32x32/mimetypes/application-x-tomb-volume.png - ln -s $(AppInstIcondir)/monmort.png \ + ln -sf $(AppInstIcondir)/monmort.png \ $(DESTDIR)/$(datadir)/icons/hicolor/32x32/mimetypes/application-x-tomb-key.png @if ! [ -n `which update-icon-caches` ]; then \ update-icon-caches $(DESTDIR)/$(datadir)/icons/hicolor; fi install $(top_builddir)/share/gtkrc $(DESTDIR)/$(GtkRcdir)/ distclean: unlink gtkrc - unlink Makefile- \ No newline at end of file + unlink Makefile diff --git a/src/tomb b/src/tomb @@ -4,7 +4,8 @@ # # a tool to easily operate file encryption of private and secret data # -# Copyleft (C) 2007-2011 Denis Roio <jaromil@dyne.org> +# {{{ Copyleft (C) 2007-2011 Denis Roio <jaromil@dyne.org> + # # This source code is free software; you can redistribute it and/or # modify it under the terms of the GNU Public License as published by @@ -20,10 +21,12 @@ # this source code; if not, write to: # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# }}} +# {{{ GLOBAL VARIABLES VERSION=1.2 DATE=Nov/2011 TOMBEXEC=$0 -TOMBOPENEXEC="tomb-open" +TOMBOPENEXEC="${TOMBEXEC}-open" typeset -a OLDARGS for arg in ${argv}; do OLDARGS+=($arg); done STEGHIDE=1 @@ -36,128 +39,190 @@ DEBUG=0 typeset -A global_opts typeset -A opts -# PATH=/usr/bin:/usr/sbin:/bin:/sbin +# Set a sensible PATH +PATH=/sbin:/bin:/usr/sbin:/usr/bin +[[ "$TOMBEXEC" =~ "^/usr/local" ]] && PATH="/usr/local/bin:/usr/local/sbin:$PATH" -# standard output message routines -# it's always useful to wrap them, in case we change behaviour later -notice() { if [[ $QUIET == 0 ]]; then print "$fg_bold[green][*]$fg_no_bold[white] $1" >&2; fi } -error() { if [[ $QUIET == 0 ]]; then print "$fg[red][!]$fg[white] $1" >&2; fi } -func() { if [[ $DEBUG == 1 ]]; then print "$fg[blue][D]$fg[white] $1" >&2; fi } -act() { - if [[ $QUIET == 0 ]]; then - if [ "$1" = "-n" ]; then - print -n "$fg_bold[white] . $fg_no_bold[white] $2" >&2; - else - print "$fg_bold[white] . $fg_no_bold[white] $1" >&2; - fi - fi +# PATH=/usr/bin:/usr/sbin:/bin:/sbin +# }}} +# {{{ HELPER FUNCTIONS + +# {{{ - Standard output message routines + +function _msg() { + local command="print -P" + local progname="%{%F{magenta}%}${TOMBEXEC##*/}%{%f%}" + local message="%{%F{normal}%}${2}%{%f%}" + local -i returncode + + case "$1" in + inline) + command+=" -n"; pchars=" > "; pcolor="yellow" + ;; + message) + pchars=" . "; pcolor="green" + ;; + verbose) + pchars="[D]"; pcolor="yellow" + ;; + success) + pchars="(*)"; pcolor="green"; message="%{%F{$pcolor}%}${2}%{%f%}" + ;; + warning) + pchars="[W]"; pcolor="red"; message="%{%F{yellow}%}${2}%{%f%}" + ;; + failure) + pchars="[E]"; pcolor="red"; message="%{%F{$pcolor}%}${2}%{%f%}" + returncode=1 + ;; + *) + pchars="[F]"; pcolor="red" + message="Developer oops! Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\"" + returncode=127 + ;; + esac + ${=command} "${progname} %{%B%F{$pcolor}%}$pchars%{%f%b%} ${message}" + return $returncode +} +function _message say() +{ + local notice="message" + [[ "$1" = "-n" ]] && shift && notice="inline" + (( $QUIET )) || _msg "$notice" "$1" + return 0 +} +alias act="_message -n" +function _verbose xxx() +{ + (( $DEBUG )) && _msg verbose "$1" + return 0 +} +function _success yes() +{ + (( $QUIET )) || _msg success "$1" + return 0 +} +function _warning no() +{ + (( $QUIET )) || _msg warning "$1" + return 1 +} +function _failure die() +{ + typeset -i exitcode=${2:-1} + (( $QUIET )) || _msg failure "$1" + exit $exitcode } +# }}} +# {{{ - CHECK BINARY DEPENDENCIES + check_bin() { + # check for required programs + for req in cryptsetup pinentry sudo; do + which $req >/dev/null || die "Cannot find $req. Please install it." 1 + done + # which dd command to use - which dcfldd > /dev/null - if [ $? = 0 ]; then - DD="dcfldd" - else - DD=dd - fi + which dcfldd > /dev/null && DD=dcfldd || DD=dd # which wipe command to use - which wipe > /dev/null - if [ $? = 0 ]; then - WIPE=(wipe -f -s) - else - WIPE=(rm -f) - fi + which wipe > /dev/null && WIPE="wipe -f -s" || WIPE="rm -f" # check for filesystem creation progs - which mkfs.ext4 > /dev/null - if [ $? = 0 ]; then - MKFS=(mkfs.ext4 -q -F -j -L) - else - MKFS=(mkfs.ext3 -q -F -j -L) - fi - - # check for sudo - which sudo > /dev/null - if [ $? != 0 ]; then - error "Cannot find sudo. Please install it" - exit 1 - fi + which mkfs.ext4 > /dev/null && \ + MKFS="mkfs.ext4 -q -F -j -L" || \ + MKFS="mkfs.ext3 -q -F -j -L" + # check for mktemp + which mktemp > /dev/null || MKTEMP=0 # check for steghide - which steghide > /dev/null - if [ $? != 0 ]; then - STEGHIDE=0 - fi + which steghide > /dev/null || STEGHIDE=0 +} - which cryptsetup > /dev/null - if [ $? != 0 ]; then - error "Cannot find cryptsetup. Please install it." - exit 1 - fi +# }}} +# {{{ - "SAFE" FUNCTIONS +# {{{ - Create a directory with caution - which pinentry > /dev/null - if [ $? != 0 ]; then - error "Cannot find pinentry. Please install it." - exit 1 - fi - - which mktemp > /dev/null - if [ $?! = 0 ]; then - MKTEMP=0 - fi - - # check for tomb-open script - if [ "$0" = "./tomb" ]; then - TOMBOPENEXEC="./tomb-open" - elif [ "$0" != "tomb" ]; then - TOMBOPENEXEC="`dirname $0`/tomb-open" - fi -} +_have_shm() { + # Check availability of 1MB of SHM + xxx "_have_shm 0 We need only 1 MB of RAM" + [[ -k /dev/shm ]] || return 1 -# safe dir creation function -safe_dir() { - which mktemp &> /dev/null - if [[ $? = 0 ]]; then - mktemp -d /dev/shm/$1.$$.XXXXXXX - return - fi - dir="/dev/shm/$1.$$.$RANDOM.$RANDOM" - (umask 077 && mkdir "$dir") || print "-1" - print "$dir" -} + local -i SHM RAM + SHM=$(df -k -B 4K -a -t tmpfs /dev/shm | awk '/\/dev\/shm/ { print $4; }') + (( $? )) && return 1 + xxx "_have_shm 1 SHM $SHM KB are available" -safe_file() { - local tmpdir tmpfile - - if [ "$MKTEMP" = "1" ]; then - mktemp -u /dev/shm/$1.$$.XXXXXXX - # this return needs to output ONLY the file - else - tmpfile="/dev/shm/$1.$$.$RANDOM.$RANDOM" - print $tmpfile - fi + RAM=$(awk '/MemFree/ { print $2 }' /proc/meminfo) + xxx "_have_shm 2 RAM $RAM KB are free" + (( $RAM >= 1024 )) && return 0 + + xxx "_have_shm 3 RAM $RAM KB left only :(" + # Now we have more RAM than affected to SHM, so we can expect some for our little needs. + # Does that work when SHM is disabled from kernel config? + return 1 } -#check if there is swap activated +safe_dir() { + # Try and create our temporary directory in RAM + # Note that there's no warranty the underlying FS won't swap + # every 5 seconds (e.g., ext3) + local -i tries + while (( $tries < 3 )) ; do + tries+=1 + if _have_shm; then + xxx "safe_dir creating directory in RAM" + if (( $MKTEMP )); then + mktemp -d /dev/shm/$1.$$.XXXXXXX + else + 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)" + sync && sleep 0.5 + fi + done + _warning "WARNING: no RAM available for me to run safely." + return 1 +} +# }}} +# {{{ - Provide a random filename in shared memory +safe_filename() { + _have_shm || die "No access to shared memory on this system, sorry." + (( $MKTEMP )) && \ + mktemp -u /dev/shm/$1.$$.XXXXXXX || \ + print "/dev/shm/$1.$$.$RANDOM$RANDOM" +} +# }}} +# {{{ - Check if swap is activated check_swap() { # Return 0 if NO swap is used, 1 if swap is used - # TODO: it should return 2 if swap is used, but encrypted - nlines=$(wc -l /proc/swaps|cut -f1 -d ' ') - if [[ $nlines -gt 1 ]]; then - r=1 - else - #and return 2 - r=0 - fi - if [[ $1 == out ]]; then - echo $r; - fi - return $r; + # Return 2 if swap(s) is(are) used, but ALL encrypted + local swaps=$(awk '/partition/ { print $1 }' /proc/swaps 2>/dev/null) + [[ -z "$swaps" ]] && return 0 # No swap partition is active + + local -i count + xxx "check_swap $swaps" + for dev in ${=swaps} + do + xxx "check_swap testing device $dev (count = $count)" + sudo cryptsetup status ${dev} | grep 'active' &>/dev/null && count+=1 + done + xxx "$count encrypted swap(s) [${(%w)#swaps} total]" + (( ${count} == ${(%w)#swaps} )) && return 2 # All swap partitions are encrypted + xxx "check_swap detected some unencrypted swap" + (( ${#{=swaps}} )) && return 1 # Some unencrypted swap partition(s) + xxx "check_swap should never reach here" + return 0 # No swap partition is active } - +# }}} +# {{{ - Ask user for a password # we use pinentry now # comes from gpg project and is much more secure # it also conveniently uses the right toolkit @@ -184,24 +249,23 @@ GETPIN EOF } - -# drop privileges +# }}} +# {{{ - Drop privileges exec_as_user() { if ! [ $SUDO_USER ]; then exec $@[@] return $? fi - func "exec_as_user '$SUDO_USER': ${(f)@}" + xxx "exec_as_user '$SUDO_USER': ${(f)@}" sudo -u $SUDO_USER "${@[@]}" return $? } - - -# escalate privileges +# }}} +# {{{ - Escalate privileges check_priv() { if [ $UID != 0 ]; then - func "Using sudo for root execution of 'tomb ${(f)OLDARGS}'" + xxx "Using sudo for root execution of 'tomb ${(f)OLDARGS}'" # check if sudo has a timestamp active sudok=false sudo -n ${TOMBEXEC} &> /dev/null @@ -220,9 +284,11 @@ EOF fi # are we root already return 0 } - +# }}} +# }}} +# {{{ - TOMB USAGE usage() { - cat <<EOF + cat <<EOF Tomb $VERSION - a strong and gentle undertaker for your secrets Copyright (C) 2007-2011 Dyne.org Foundation, License GNU GPL v3+ @@ -240,13 +306,13 @@ Commands: slam close tomb FILE and kill all pids using it passwd change the password of a tomb key FILE EOF -if [ "$STEGHIDE" = 1 ]; then - cat <<EOF + if [ "$STEGHIDE" = 1 ]; then + cat <<EOF bury hide a tomb key FILE inside a jpeg PLACE exhume extract a tomb key FILE from a jpeg PLACE EOF -fi -cat <<EOF + fi + cat <<EOF Options: @@ -264,7 +330,8 @@ For more informations on Tomb read the manual: man tomb Please report bugs on <http://bugs.dyne.org>. EOF } - +# }}} +# {{{ - I18N FUNCTIONS generate_translatable_strings() { cat <<EOF # Tomb - The Crypto Undertaker. @@ -291,7 +358,7 @@ EOF usage | awk ' { print "\"" $0 "\"" }' -cat <<EOF + cat <<EOF msgstr "" # @@ -301,25 +368,216 @@ msgstr "" EOF cat $TOMBEXEC | awk ' -/notice ".*"$/ { sub( /notice/ , ""); - print "#: notice"; print "msgid " $0; print "msgstr \"\"\n" } +/(_verbose|xxx) ".*"$/ { sub( /^(_verbose|xxx)/ , ""); + print "#: _verbose"; print "msgid " $0; print "msgstr \"\"\n" } + +/(_success|yes) ".*"$/ { sub( /^(_success|yes)/ , ""); + print "#: _success"; print "msgid " $0; print "msgstr \"\"\n" } + +/(_warning|no) ".*"$/ { sub( /^(_warning|no)/ , ""); + print "#: _warning"; print "msgid " $0; print "msgstr \"\"\n" } -/act ".*"$/ { sub( /act/ , ""); - print "#: act"; print "msgid " $0; print "msgstr \"\"\n" } +/(_failure|die) ".*"$/ { sub( /^(_failure|die)/ , ""); + print "#: _failure"; print "msgid " $0; print "msgstr \"\"\n" } + +/(_message|say) ".*"$/ { sub( /^(_message|say)/ , ""); + print "#: _message"; print "msgid " $0; print "msgstr \"\"\n" } + +/(_message -n|act) ".*"$/ { sub( /^(_message -n|act)/ , ""); + print "#: _message -n"; print "msgid " $0; print "msgstr \"\"\n" } ' } +# }}} +# {{{ - HELPERS FOR KEYS +# {{{ - Encode Key +encode_key() { + tombkey=$CMD2 + imagefile=$CMD3 + + file $tombkey | grep PGP > /dev/null + if [ $? != 0 ]; then + _warning "encode failed: $tombkey is not a tomb key" + return 1 + fi + file $imagefile | grep JPEG > /dev/null + if [ $? != 0 ]; then + _warning "encode failed: $imagefile is not a jpeg image" + return 1 + fi + + _success "Encoding key $tombkey inside image $imagefile" + _message "please choose a password for the encoding" + + # here user is prompted for key password + for c in 1 2 3; do + # 3 tries to write two times a matching password + tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey}"` + tombpasstmp=$tombpass + tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey} (again)"` + if [ "$tombpasstmp" = "$tombpass" ]; then + break; + fi + unset tombpasstmp + unset tombpass + done + + if [ -z $tombpass ]; then + _warning "passwords don't match, aborting operation." + return 1 + fi + + awk ' +/^-----/ {next} +/^Version/ {next} +{print $0}' ${tombkey} \ + | steghide embed --embedfile - --coverfile ${imagefile} \ + -p ${tombpass} -z 9 -e serpent cbc + if [ $? != 0 ]; then + _warning "encoding error: steghide reports problems" + res=1 + else + _success "tomb key encoded succesfully into image ${imagefile}" + res=0 + fi + + unset tombpass + + return $res +} +# }}} +# {{{ - Decode Key +decode_key() { + tombname=$CMD2 + imagefile=$CMD3 + res=1 + + file $imagefile | grep JPEG > /dev/null + if [ $? != 0 ]; then + _warning "encode failed: $imagefile is not a jpeg image" + return 1 + fi + + keyfile=${tombname%%\.*}.tomb.key + if [[ -e "$keyfile" ]]; then + _warning "Key file $keyfile already exist." + return 1 + fi + _message "Trying to exhume a key out of image $imagefile" + for c in 1 2 3; do + if [ $c = 1 ]; then + tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${keyfile}"` + else + tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for $keyfile (retry $c)"` + fi + steghide extract -sf ${imagefile} -p ${tombpass} -xf - \ + | awk ' +BEGIN { +print "-----BEGIN PGP MESSAGE-----" +} +{ print $0 } +END { +print "-----END PGP MESSAGE-----" +}' > ${keyfile} + + if [ "`cat ${keyfile} | wc -l`" != "3" ]; then + _success "${keyfile} succesfully decoded" + res=0 + break; + fi + done + + unset tombpass + + if [ $res != 0 ]; then + _warning "nothing found." + fi + + return $res +} +# }}} +# }}} +# {{{ - HOOK HELPERS +# {{{ - Execute Bind Hooks + +exec_safe_bind_hooks() { + if [[ -n ${(k)opts[-o]} ]]; then + MOUNTOPTS=${opts[-o]} + fi + local MOUNTPOINT="${1}" + local ME=${SUDO_USER:-$(whoami)} + local HOME=$(awk -v a="$ME" -F ':' '{if ($1 == a) print $6}' /etc/passwd 2>/dev/null) + if [ $? -ne 0 ]; then + _warning "how pitiful! A tomb, and no HOME" + return 1 + fi + if [ -z "$MOUNTPOINT" -o ! -d "$MOUNTPOINT" ]; then + _warning "cannot exec bind hooks without a mounted tomb." + return 1 + fi + if ! [ -r "$MOUNTPOINT/bind-hooks" ]; then + xxx "bind-hooks not found in $MOUNTPOINT" + return 1 + fi + typeset -al mounted + typeset -Al maps + maps=($(<"$MOUNTPOINT/bind-hooks")) + for dir in ${(k)maps}; do + if [ "${dir[1]}" = "/" -o "${dir[1,2]}" = ".." ]; then + _warning "bind-hooks map format: local/to/tomb local/to/\$HOME" + continue + fi + if [ "${${maps[$dir]}[1]}" = "/" -o "${${maps[$dir]}[1,2]}" = ".." ]; then + _warning "bind-hooks map format: local/to/tomb local/to/\$HOME. Rolling back" + for dir in ${mounted}; do umount $dir; done + return 1 + fi + if [ ! -r "$HOME/${maps[$dir]}" ]; then + _warning "bind-hook target not existent, skipping $HOME/${maps[$dir]}" + elif [ ! -r "$MOUNTPOINT/$dir" ]; then + _warning "bind-hook source not found in tomb, skipping ${MOUNTPOINT}/${dir}" + else + mount -o bind,$MOUNTOPTS $MOUNTPOINT/$dir $HOME/${maps[$dir]} + mounted+=("$HOME/${maps[$dir]}") + fi + done +} + +# }}} +# {{{ - POST-MOUNT HOOKS +exec_safe_post_hooks() { + local mnt=$1 # first argument is where the tomb is mounted + local ME=${SUDO_USER:-$(whoami)} + if ! [ -x ${mnt}/post-hooks ]; then return; fi + # if 'post-hooks' is found inside the tomb, check it: if it is an + # executable, launch it as a user this might need a dialog for + # security on what is being run, however we expect you know well + # what is inside your tomb. this feature opens the possibility to + # make encrypted executables. + cat ${mnt}/post-hooks | head -n1 | grep '^#!/' + if [ $? = 0 ]; then + _success "post hooks found, executing as user $SUDO_USER" + exec_as_user ${mnt}/post-hooks $2 + fi +} +# }}} +# }}} + +# }}} +# {{{ TOMB SUB-COMMANDS + +# {{{ - Create create_tomb() { if ! option_is_set --ignore-swap && [[ `check_swap out` == 1 ]]; then - error "You have swap activated; use --ignore-swap if you want to skip this check" - act "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." - act "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" - act "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" + _warning "You have swap activated; use --ignore-swap if you want to skip this check" + _message "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." + _message "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" + _message "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" tail -n +2 /proc/swaps exit 1 fi if ! [ ${CMD2} ]; then - error "no tomb name specified for creation" + _warning "no tomb name specified for creation" return 1 fi @@ -328,19 +586,19 @@ create_tomb() { # make sure the file has a .tomb extension tombname=${tombfile%%\.*} tombfile=${tombname}.tomb - tombsize=$opts[-s] - + tombsize=$opts[-s] + if [[ $tombsize != <-> ]]; then - error "Size is not an integer" + _warning "Size is not an integer" return 1 fi - + if [ -e ${tombdir}/${tombfile} ]; then - error "tomb exists already. I'm not digging here:" - ls -lh ${tombdir}/${tombfile} - return 1 + _warning "tomb exists already. I'm not digging here:" + ls -lh ${tombdir}/${tombfile} + return 1 fi - + if option_is_set -k; then tombkey="`option_value -k`.tomb.key" else @@ -348,29 +606,28 @@ create_tomb() { fi if [ -e "${tombkey}" ]; then - error "tomb key already exists. Quitting." + _warning "tomb key already exists. Quitting." ls -lh ${tombkey} return 1 fi - notice "Creating a new tomb in ${tombdir}/${tombfile}" + _success "Creating a new tomb in ${tombdir}/${tombfile}" if [ -z $tombsize ]; then - act "No size specified, summoning the Tomb Undertaker to guide us in the creation." + _message "No size specified, summoning the Tomb Undertaker to guide us in the creation." "$TOMBOPENEXEC" & wait $! return 0 fi tombsize_4k=`expr $tombsize \* 1024 / 4` - act "Generating ${tombfile} of ${tombsize}Mb (${tombsize_4k} blocks of 4Kb)" + _message "Generating ${tombfile} of ${tombsize}Mb (${tombsize_4k} blocks of 4Kb)" $DD if=/dev/urandom bs=4k count=${tombsize_4k} of=${tombdir}/${tombfile} if [ $? = 0 -a -e ${tombdir}/${tombfile} ]; then - act "OK: `ls -lh ${tombdir}/${tombfile}`" + _success "OK: `ls -lh ${tombdir}/${tombfile}`" else - error "Error creating the tomb ${tombdir}/${tombfile}, operation aborted." - exit 1 + die "Error creating the tomb ${tombdir}/${tombfile}, operation aborted." fi nstloop=`losetup -f` # get the number for next loopback device @@ -378,25 +635,23 @@ create_tomb() { # create the keyfile in tmpfs so that we leave less traces in RAM keytmp=`safe_dir tomb` - if [ "$keytmp" = "-1" ]; then - error "error creating temp dir" - exit 1 - fi + (( $? )) && die "error creating temp dir" + xxx "safe_dir at $keytmp" + #rm -f $keytmp # ?????? creo, cancello e ricreo ?????? #mkdir -p $keytmp mount tmpfs "${keytmp}" -t tmpfs -o size=1m if [ $? != 0 ]; then - error "cannot mount tmpfs filesystem in volatile memory" - error "operation aborted." + _warning "cannot mount tmpfs filesystem in volatile memory" losetup -d $nstloop rm -r "${keytmp}" - exit 1 + die "operation aborted." fi - act "Generating secret key..." - act "this operation takes time, keep using this computer on other tasks," - act "once done you will be asked to choose a password for your tomb." - act "To make it faster you can move the mouse around" + _message "Generating secret key..." + _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 "To make it faster you can move the mouse around" touch ${keytmp}/tomb.tmp chmod 0600 ${keytmp}/tomb.tmp if [[ $DD = "dcfldd" ]]; then @@ -405,14 +660,14 @@ create_tomb() { $DD bs=1 count=256 if=/dev/random of=${keytmp}/tomb.tmp fi if ! [ -r ${keytmp}/tomb.tmp ]; then - error "cannot generate encryption key, operation aborted." + _warning "cannot generate encryption key" umount ${keytmp} losetup -d $nstloop rm -r $keytmp - exit 1 + die "operation aborted." fi - notice "Setup your secret key file ${tombkey}" + _success "Setup your secret key file ${tombkey}" # here user is prompted for key password for c in 1 2 3; do @@ -428,11 +683,10 @@ create_tomb() { done if [ -z $tombpass ]; then - error "passwords don't match, aborting operation" umount ${keytmp} losetup -d $nstloop rm -r $keytmp - exit 1 + die "passwords don't match, aborting operation" fi @@ -441,38 +695,38 @@ create_tomb() { -o "${tombkey}" -c -a ${keytmp}/tomb.tmp <<< ${tombpass} # if [ $? != 0 ]; then - # error "setting password failed: gnupg returns 2" + # _warning "setting password failed: gnupg returns 2" # umount ${keytmp} # losetup -d $nstloop # rm -r $keytmp # exit 1 # fi - act "formatting Luks mapped device" + _message "formatting Luks mapped device" # we use aes-cbc-essiv with sha256 # for security, performance and compatibility + # XXX: More for compatibility then, because xts-plain is better nowadays. cryptsetup --batch-mode \ --cipher aes-cbc-essiv:sha256 --key-size 256 \ luksFormat ${nstloop} ${keytmp}/tomb.tmp if ! [ $? = 0 ]; then - act "operation aborted." - exit 0 + die "operation aborted." 0 fi cryptsetup --key-file ${keytmp}/tomb.tmp --cipher aes luksOpen ${nstloop} tomb.tmp - ${WIPE[@]} ${keytmp}/tomb.tmp + ${=WIPE} ${keytmp}/tomb.tmp umount ${keytmp} rm -r ${keytmp} # cryptsetup luksDump ${nstloop} - act "formatting your Tomb with Ext3/Ext4 filesystem" - ${MKFS} ${tombname} /dev/mapper/tomb.tmp + _message "formatting your Tomb with Ext3/Ext4 filesystem" + ${=MKFS} ${tombname} /dev/mapper/tomb.tmp if [ $? != 0 ]; then - error "Tomb format returns error" - error "your tomb ${tombfile} maybe corrupt" + _warning "Tomb format returned an error:" + _warning "your tomb ${tombfile} may be corrupted." fi sync @@ -485,23 +739,26 @@ create_tomb() { chmod 0600 "${tombdir}/${tombfile}" chown $(id -u $ME):$(id -g $ME) "${tombdir}/${tombfile}" - act "done creating $tombname encrypted storage (using Luks dm-crypt AES/SHA256)" - notice "Your tomb is ready in ${tombdir}/${tombfile} and secured with key ${tombkey}" + _message "done creating $tombname encrypted storage (using Luks dm-crypt AES/SHA256)" + _success "Your tomb is ready in ${tombdir}/${tombfile} and secured with key ${tombkey}" } +# }}} +# {{{ - Open + mount_tomb() { - notice "Commanded to open tomb $CMD2" + _message "Commanded to open tomb $CMD2" if ! option_is_set --ignore-swap && [[ `check_swap out` == 1 ]]; then - error "You have swap activated; use --ignore-swap if you want to skip this check" - act "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." - act "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" - act "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" + _warning "You have swap activated; use --ignore-swap if you want to skip this check" + _message "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." + _message "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" + _message "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" tail -n +2 /proc/swaps - exit 1 + die "I'm stopping now." fi if ! [ ${CMD2} ]; then - error "no tomb name specified for creation" + _warning "no tomb name specified for creation" return 1 fi @@ -516,11 +773,11 @@ mount_tomb() { # check file type (if its a Luks fs) file ${tombdir}/${tombfile} | grep -i 'luks encrypted file' 2>&1 >/dev/null if [ $? != 0 ]; then - error "$CMD2 is not a valid tomb file, operation aborted" + _warning "$CMD2 is not a valid tomb file, operation aborted" return 1 fi tombname=${tombfile%%\.*} - func "tomb found: ${tombdir}/${tombfile}" + xxx "tomb found: ${tombdir}/${tombfile}" if option_is_set -k ; then if [[ "`option_value -k`" == "-" ]]; then @@ -538,47 +795,46 @@ mount_tomb() { tombkey=${tombdir}/${tombfile}.key fi if ! [ -r ${tombkey} ]; then - error "key file not found: ${tombkey}" - error "operation aborted." + _warning "key file not found: ${tombkey}" + _warning "operation aborted." return 1 fi if ! [ $CMD3 ]; then tombmount=/media/${tombfile} - act "mountpoint not specified, using default: $tombmount" + _message "mountpoint not specified, using default: $tombmount" elif ! [ -x $CMD3 ]; then - error "mountpoint $CMD3 doesn't exist, operation aborted." + _warning "mountpoint $CMD3 doesn't exist, operation aborted." return 1 else tombmount=${CMD3} fi - + # check if its already open mount -l | grep "${tombfile}.*\[$tombname\]$" 2>&1 > /dev/null if [ $? = 0 ]; then - error "$tombname is already open on $tombmount" - act "here below its status is reported:" + _warning "$tombname is already open on $tombmount" + _message "here below its status is reported:" list_tombs ${tombname} return 1 fi - - notice "mounting $tombfile on mountpoint $tombmount" + + _success "mounting $tombfile on mountpoint $tombmount" # we need root from here on mkdir -p $tombmount nstloop=`losetup -f` if [ $? = 255 ]; then - error "too many tomb opened. Please close any of them to open another tomb" - exit 1 + die "too many tomb opened. Please close any of them to open another tomb" fi losetup -f ${tombdir}/${tombfile} - act "check for a valid LUKS encrypted device" + _message "check for a valid LUKS encrypted device" cryptsetup isLuks ${nstloop} if [ $? != 0 ]; then # is it a LUKS encrypted nest? see cryptsetup(1) - error "$tombfile is not a valid Luks encrypted storage file" + _warning "$tombfile is not a valid Luks encrypted storage file" $norm || rmdir $tombmount 2>/dev/null return 1 fi @@ -589,21 +845,21 @@ mount_tomb() { mapper="tomb.${tombname}.${mapdate}.`basename $nstloop`" keyname=`basename $tombkey | cut -d. -f1` - notice "Password is required for key ${keyname}" + _success "Password is required for key ${keyname}" for c in 1 2 3; do if [ $c = 1 ]; then tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb ${keyname}"` else tombpass=`exec_as_user ${TOMBEXEC} askpass "Open tomb $keyname (retry $c)"` fi - (gpg --batch --passphrase-fd 0 --no-tty --no-options \ - -d "${tombkey}" 2> /dev/null <<< ${tombpass} ) \ - | cryptsetup --key-file - luksOpen ${nstloop} ${mapper} + (gpg --batch --passphrase-fd 0 --no-tty --no-options \ + -d "${tombkey}" 2> /dev/null <<< ${tombpass} ) \ + | cryptsetup --key-file - luksOpen ${nstloop} ${mapper} unset tombpass # if key was from stdin delete temp file and dir if [ $tombkeydir ]; then - ${WIPE[@]} ${tombkey} + ${=WIPE} ${tombkey} rmdir $tombkeydir fi @@ -613,15 +869,15 @@ mount_tomb() { done if ! [ -r /dev/mapper/${mapper} ]; then - error "failure mounting the encrypted file" + _warning "failure mounting the encrypted file" losetup -d ${nstloop} $norm || rmdir ${tombmount} 2>/dev/null return 1 fi - act "encrypted storage filesystem check" + _message "encrypted storage filesystem check" fsck -p -C0 /dev/mapper/${mapper} - func "tomb engraved as $tombname" + xxx "tomb engraved as $tombname" tune2fs -L ${tombname} /dev/mapper/${mapper} > /dev/null mount -o $MOUNTOPTS /dev/mapper/${mapper} ${tombmount} @@ -631,7 +887,7 @@ mount_tomb() { chmod 0750 ${tombmount} chown $(id -u $ME):$(id -g $ME) ${tombmount} - notice "encrypted storage $tombfile succesfully mounted on $tombmount" + _success "encrypted storage $tombfile succesfully mounted on $tombmount" if ! option_is_set -n ; then exec_safe_bind_hooks ${tombmount} exec_safe_post_hooks ${tombmount} open @@ -639,357 +895,146 @@ mount_tomb() { return 0 } -encode_key() { - tombkey=$CMD2 - imagefile=$CMD3 - - file $tombkey | grep PGP > /dev/null - if [ $? != 0 ]; then - error "encode failed: $tombkey is not a tomb key" - return 1 - fi - file $imagefile | grep JPEG > /dev/null - if [ $? != 0 ]; then - error "encode failed: $imagefile is not a jpeg image" - return 1 - fi - - notice "Encoding key $tombkey inside image $imagefile" - act "please choose a password for the encoding" - - # here user is prompted for key password - for c in 1 2 3; do - # 3 tries to write two times a matching password - tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey}"` - tombpasstmp=$tombpass - tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${tombkey} (again)"` - if [ "$tombpasstmp" = "$tombpass" ]; then - break; - fi - unset tombpasstmp - unset tombpass - done - - if [ -z $tombpass ]; then - error "passwords don't match, aborting operation." - return 1 - fi - - awk ' -/^-----/ {next} -/^Version/ {next} -{print $0}' ${tombkey} \ - | steghide embed --embedfile - --coverfile ${imagefile} \ - -p ${tombpass} -z 9 -e serpent cbc - if [ $? != 0 ]; then - error "encoding error: steghide reports problems" - res=1 - else - notice "tomb key encoded succesfully into image ${imagefile}" - res=0 - fi - - unset tombpass - - return $res -} - -decode_key() { - tombname=$CMD2 - imagefile=$CMD3 - res=1 - - file $imagefile | grep JPEG > /dev/null - if [ $? != 0 ]; then - error "encode failed: $imagefile is not a jpeg image" - return 1 - fi - - keyfile=${tombname%%\.*}.tomb.key - if [[ -e "$keyfile" ]]; then - error "Key file $keyfile already exist." - return 1 - fi - notice "Trying to exhume a key out of image $imagefile" - for c in 1 2 3; do - if [ $c = 1 ]; then - tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for ${keyfile}"` - else - tombpass=`exec_as_user ${TOMBEXEC} askpass "Steg password for $keyfile (retry $c)"` - fi - steghide extract -sf ${imagefile} -p ${tombpass} -xf - \ - | awk ' -BEGIN { -print "-----BEGIN PGP MESSAGE-----" -} -{ print $0 } -END { -print "-----END PGP MESSAGE-----" -}' > ${keyfile} - - if [ "`cat ${keyfile} | wc -l`" != "3" ]; then - act "${keyfile} succesfully decoded" - res=0 - break; - fi - done - - unset tombpass - - if [ $res != 0 ]; then - error "nothing found." - fi - - return $res -} - -exec_safe_bind_hooks() { - if [[ -n ${(k)opts[-o]} ]]; then - MOUNTOPTS=${opts[-o]} - fi - local MOUNTPOINT="${1}" - local ME=${SUDO_USER:-$(whoami)} - local HOME=$(awk -v a="$ME" -F ':' '{if ($1 == a) print $6}' /etc/passwd 2>/dev/null) - if [ $? -ne 0 ]; then - error "how pitiful! A tomb, and no HOME" - return 1 - fi - if [ -z "$MOUNTPOINT" -o ! -d "$MOUNTPOINT" ]; then - error "cannot exec bind hooks without a mounted tomb." - return 1 - fi - if ! [ -r "$MOUNTPOINT/bind-hooks" ]; then - func "bind-hooks not found in $MOUNTPOINT" - return 1 - fi - typeset -al mounted - typeset -Al maps - maps=($(<"$MOUNTPOINT/bind-hooks")) - for dir in ${(k)maps}; do - if [ "${dir[1]}" = "/" -o "${dir[1,2]}" = ".." ]; then - error "bind-hooks map format: local/to/tomb local/to/\$HOME" - continue - fi - if [ "${${maps[$dir]}[1]}" = "/" -o "${${maps[$dir]}[1,2]}" = ".." ]; then - error "bind-hooks map format: local/to/tomb local/to/\$HOME. Rolling back" - for dir in ${mounted}; do umount $dir; done - return 1 - fi - if [ ! -r "$HOME/${maps[$dir]}" ]; then - error "bind-hook target not existent, skipping $HOME/${maps[$dir]}" - elif [ ! -r "$MOUNTPOINT/$dir" ]; then - error "bind-hook source not found in tomb, skipping ${MOUNTPOINT}/${dir}" - else - mount -o bind,$MOUNTOPTS $MOUNTPOINT/$dir $HOME/${maps[$dir]} - mounted+=("$HOME/${maps[$dir]}") - fi - done -} - -exec_safe_post_hooks() { - local mnt=$1 # first argument is where the tomb is mounted - local ME=${SUDO_USER:-$(whoami)} - if ! [ -x ${mnt}/post-hooks ]; then return; fi - # if 'post-hooks' is found inside the tomb, check it: if it is an - # executable, launch it as a user this might need a dialog for - # security on what is being run, however we expect you know well - # what is inside your tomb. this feature opens the possibility to - # make encrypted executables. - cat ${mnt}/post-hooks | head -n1 | grep '^#!/' - if [ $? = 0 ]; then - act "post hooks found, executing as user $SUDO_USER" - exec_as_user ${mnt}/post-hooks $2 - fi -} - -kill_tomb() { - # $1 = pids to kill - # $2 = type of kill - local e p - # substitute the \n with space - e=${1//$'\n'/ } - # split the string at space delim - # so we can get a for loop honestly - e=(${(s: :)e}) - for p in $e; do - func "killing PID $p..." - if [[ "$2" == "soft" ]]; then - kill -USR1 $p - elif [[ "$2" == "hard" ]]; then - kill -TERM $p - elif [[ "$2" == "must die" ]]; then - kill -KILL $p - fi - done -} - +# }}} +# {{{ - Close +# {{{ - Slam the door +# Kill all processes using the tomb slam_tomb() { # $1 = tomb mount point - local pidk - - pidk=`lsof -t "$1"` - if [[ ! -z "$pidk" ]]; then - kill_tomb "$pidk" "soft" - else - return 0 - fi - - # if there are remaining pids - # we need to play hard - pidk=`lsof -t "$1"` - if [[ -z "$pidk" ]]; then - return 0 - fi - - # if we set the -f (force) option - # don't wait, just kill - option_is_set -f || sleep 3 - kill_tomb "$pidk" "hard" - pidk=`lsof -t "$1"` - if [[ -z "$pidk" ]]; then - return 0 - fi - - # if there are still some pids around - # we have to kill 'em all - option_is_set -f || sleep 3 - kill_tomb "$pidk" "must die" - pidk=`lsof -t "$1"` - if [[ -z "$pidk" ]]; then - return 0 - fi - - # what PITA! + for s in INT TERM HUP KILL; do + fuser -s -m "$1" || return 0 + fuser -s -m "$1" -k -M -$s && { option_is_set -f || sleep 3 } + done return 1 } - +# }}} +# {{{ - Unmount Tomb umount_tomb() { local tombs how_many_tombs local pathmap mapper tombname tombmount loopdev local ans pidk pname if ! [ $1 ]; then - tombs=`find /dev/mapper -name 'tomb.*'` - how_many_tombs=`wc -w <<< "$tombs"` - if [[ "$how_many_tombs" == "0" ]]; then - error "There is no open tomb to be closed" - return 1 - elif [[ "$how_many_tombs" == "1" ]]; then - #mapper=`find /dev/mapper -name 'tomb.*'` - func "closing mapper $tombs" - umount_tomb ${tombs} - return 1 - else - error "Too many tombs mounted, please specify which to unmount:" - ls /dev/mapper/tomb.* - error "or issue the command 'tomb close all' to clos'em all." - return 1 - fi + tombs=`find /dev/mapper -name 'tomb.*'` + how_many_tombs=`wc -w <<< "$tombs"` + if [[ "$how_many_tombs" == "0" ]]; then + _warning "There is no open tomb to be closed" + return 1 + elif [[ "$how_many_tombs" == "1" ]]; then + #mapper=`find /dev/mapper -name 'tomb.*'` + xxx "closing mapper $tombs" + umount_tomb ${tombs} + return 1 + else + _warning "Too many tombs mounted, please specify which to unmount:" + ls /dev/mapper/tomb.* + _warning "or issue the command 'tomb close all' to clos'em all." + return 1 + fi fi if [ "$1" = "all" ]; then - tombs=`find /dev/mapper -name 'tomb.*'` - if ! [ $tombs ]; then - notice "Tombs are all closed, cemetery is quiet." - return 0 - fi - for t in ${(f)tombs}; do - umount_tomb ${t} - done + tombs=`find /dev/mapper -name 'tomb.*'` + if ! [ $tombs ]; then + _success "Tombs are all closed, cemetery is quiet." return 0 + fi + for t in ${(f)tombs}; do + umount_tomb ${t} + done + return 0 fi - # tomb close argument deduction pathmap=`dirname "$1"` if [ "${pathmap}" = "/dev/mapper" ]; then - mapper="$1" # argument is the mapper (or none which autofills mapper) - tombname="`print $mapper | cut -d. -f2`" - tombmount=`mount -l | \ + mapper="$1" # argument is the mapper (or none which autofills mapper) + tombname="`print $mapper | cut -d. -f2`" + tombmount=`mount -l | \ awk -vtomb="[$tombname]" '/^\/dev\/mapper\/tomb/ { if($7==tomb) print $3 } '` elif [ "$pathmap" = "." ]; then - tombname="$1" # argument is the name - mapper=`mount -l | \ + tombname="$1" # argument is the name + mapper=`mount -l | \ awk -vtomb="[$tombname]" '/^\/dev\/mapper\/tomb/ { if($7==tomb) print $1 } '` - tombmount=`mount -l | \ + tombmount=`mount -l | \ awk -vtomb="[$tombname]" '/^\/dev\/mapper\/tomb/ { if($7==tomb) print $3 } '` else - tombmount="$1" # argument should be the mount + tombmount="$1" # argument should be the mount mapper=`mount | awk -vmnt="$tombmount" '/^\/dev\/mapper\/tomb/ { if($3==mnt) print $1 }'` - tombname="`print $mapper | cut -d. -f2`" + tombname="`print $mapper | cut -d. -f2`" fi # avoid block when the same tomb is mounted, take only the first for tm in ${(f)tombmount}; do tombmount=${tm}; break; done - func "tomb close argument: $1" - func "name:\t$tombname" - func "mount:\t$tombmount" - func "mapper:\t$mapper" + xxx "tomb close argument: $1" + xxx "name:\t$tombname" + xxx "mount:\t$tombmount" + xxx "mapper:\t$mapper" if ! [ -e "$mapper" ]; then - error "Tomb not found: $1" - error "Please specify an existing tomb." + _warning "Tomb not found: $1" + _warning "Please specify an existing tomb." return 0 fi if [ $SLAM ]; then - notice "Slamming tomb $tombname mounted on $tombmount" - act "Kill all processes busy inside the tomb" + _success "Slamming tomb $tombname mounted on $tombmount" + _message "Kill all processes busy inside the tomb" slam_tomb "$tombmount" if [[ $? == 1 ]]; then - error "Cannot slam the tomb $tombname" + _warning "Cannot slam the tomb $tombname" return 1 fi else - notice "Closing tomb $tombname mounted on $tombmount" + _success "Closing tomb $tombname mounted on $tombmount" fi # check if there are binded dirs and close them tombmount_esc=`sed 's:\/:\\\/:g' <<< $tombmount ` unbind=`mount | awk "/^$tombmount_esc.*bind/"' { print $3 }'` for b in ${(f)unbind}; do - hook="`basename $b`" - act "closing tomb hook: $hook" - umount $b - if [[ $? != 0 ]]; then - if [ $SLAM ]; then - notice "Slamming tomb: killing all processes using this hook" + hook="`basename $b`" + _message "closing tomb hook: $hook" + umount $b + if [[ $? != 0 ]]; then + if [ $SLAM ]; then + _success "Slamming tomb: killing all processes using this hook" slam_tomb "$b" if [[ $? == 1 ]]; then - error "Cannot slam the tomb $b" + _warning "Cannot slam the tomb $b" return 1 fi - umount $b + umount $b else - error "Tomb hook is busy, cannot close tomb." - return 1 - fi + _warning "Tomb hook is busy, cannot close tomb." + return 1 fi + fi done # Execute post-hooks for eventual cleanup if ! option_is_set -n ; then - exec_safe_post_hooks ${tombmount%%/} close + exec_safe_post_hooks ${tombmount%%/} close fi if [ $tombmount ]; then # tomb is actively mounted - func "performing umount of $tombmount" + xxx "performing umount of $tombmount" umount ${tombmount} if ! [ $? = 0 ]; then - error "Tomb is busy, cannot umount!" - else + _warning "Tomb is busy, cannot umount!" + else # this means we used a "default" mount point - if [ "${tombmount}" = "/media/${tombname}.tomb" ]; then + if [ "${tombmount}" = "/media/${tombname}.tomb" ]; then rmdir ${tombmount} - fi + fi fi fi cryptsetup luksClose $mapper if ! [ $? = 0 ]; then - error "error occurred in cryptsetup luksClose ${mapper}" + _warning "error occurred in cryptsetup luksClose ${mapper}" return 1 fi @@ -1004,17 +1049,20 @@ umount_tomb() { kill ${statustray_pid} fi - notice "Tomb $tombname closed: your bones will rest in peace." + _success "Tomb $tombname closed: your bones will rest in peace." return 0 } +# }}} +# }}} +# {{{ - Change Password # change tomb key password change_passwd() { if ! option_is_set --ignore-swap && [[ `check_swap out` == 1 ]]; then - error "You have swap activated; use --ignore-swap if you want to skip this check" - act "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." - act "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" - act "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" + _warning "You have swap activated; use --ignore-swap if you want to skip this check" + _message "Using encryption with swap activated is very bad, because some files, or even your secret key, could be written on hard disk." + _message "However, it could be that your swap is encrypted. If this is case, this is ok. Then, use --ignore-swap to skip this check" + _message "You seem to be using `tail -n +2 /proc/swaps|wc -l` swaps:" tail -n +2 /proc/swaps return 1 fi @@ -1022,31 +1070,30 @@ change_passwd() { # check the keyfile if ! [ -r $keyfile ]; then - error "key not found: $keyfile" - return 1 + _warning "key not found: $keyfile" + return 1 fi - + file $keyfile | grep PGP > /dev/null if [ $? != 0 ]; then - error "file doesn't seems to be a tomb key: $keyfile" - error "operation aborted." - return 1 + _warning "file doesn't seems to be a tomb key: $keyfile" + _warning "operation aborted." + return 1 fi local tmpnewkey tmpoldkey c tombpass tombpasstmp - - tmpnewkey=`safe_file tomb` - tmpoldkey=`safe_file tomb` - - notice "Changing password for $keyfile" + tmpnewkey=`safe_filename tomb` + tmpoldkey=`safe_filename tomb` + + _success "Changing password for $keyfile" keyname=`basename $keyfile` for c in 1 2 3; do - if [ $c = 1 ]; then - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type old password for ${keyname}" "Change tomb key password"` - else - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type old password for ${keyname} (retry $c)" "Change tomb key password"` - fi + if [ $c = 1 ]; then + tombpass=`exec_as_user ${TOMBEXEC} askpass "Type old password for ${keyname}" "Change tomb key password"` + else + tombpass=`exec_as_user ${TOMBEXEC} askpass "Type old password for ${keyname} (retry $c)" "Change tomb key password"` + fi gpg --batch --no-options --no-tty --passphrase-fd 0 -o "${tmpoldkey}" -d $keyfile <<< "$tombpass" &> /dev/null if [ $? = 0 ]; then tombpass="ok" @@ -1055,35 +1102,35 @@ change_passwd() { done if [ "$tombpass" != "ok" ]; then - error "You typed an Invalid old password. Operation aborted." - # /dev/null because the file cannot exists - ${WIPE[@]} "${tmpnewkey}" 2> /dev/null - ${WIPE[@]} "${tmpoldkey}" 2> /dev/null + _warning "You typed an Invalid old password. Operation aborted." + # /dev/null because the file may not exist + ${=WIPE} "${tmpnewkey}" 2> /dev/null + ${=WIPE} "${tmpoldkey}" 2> /dev/null return 1 fi for c in 1 2 3; do - # 3 tries to write two times a matching password - if [ $c = 1 ]; then - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password for ${keyname}" "Change tomb key password"` - else - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password for ${keyname} (retry $c)" "Change tomb key password"` - fi - tombpasstmp=$tombpass - tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password again" "Change tomb key password"` - if [ "$tombpasstmp" = "$tombpass" ]; then - break; - fi - unset tombpasstmp - unset tombpass + # 3 tries to write two times a matching password + if [ $c = 1 ]; then + tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password for ${keyname}" "Change tomb key password"` + else + tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password for ${keyname} (retry $c)" "Change tomb key password"` + fi + tombpasstmp=$tombpass + tombpass=`exec_as_user ${TOMBEXEC} askpass "Type the new password again" "Change tomb key password"` + if [ "$tombpasstmp" = "$tombpass" ]; then + break; + fi + unset tombpasstmp + unset tombpass done if [ -z $tombpass ]; then - error "You mistyped the new password. Operation aborted." + _warning "You mistyped the new password. Operation aborted." # /dev/null because the file cannot exists - ${WIPE[@]} "${tmpnewkey}" 2> /dev/null - ${WIPE[@]} "${tmpoldkey}" 2> /dev/null - return 1 + ${=WIPE} "${tmpnewkey}" 2> /dev/null + ${=WIPE} "${tmpoldkey}" 2> /dev/null + return 1 fi gpg \ @@ -1091,28 +1138,30 @@ change_passwd() { -o "${tmpnewkey}" -c -a ${tmpoldkey} <<< ${tombpass} if [ $? != 0 ]; then - error "Cannot change your key passphrase" + _warning "Cannot change your key passphrase" # /dev/null because the file cannot exists - ${WIPE[@]} "${tmpnewkey}" 2> /dev/null - ${WIPE[@]} "${tmpoldkey}" 2> /dev/null + ${=WIPE} "${tmpnewkey}" 2> /dev/null + ${=WIPE} "${tmpoldkey}" 2> /dev/null return 1 fi # wipe the previous, original, key - ${WIPE[@]} "${keyfile}" + ${=WIPE} "${keyfile}" # copy the new key as the original keyfile name cp "${tmpnewkey}" "${keyfile}" - act "Cleaning environment" + _message "Cleaning environment" # wipe all temp file - ${WIPE[@]} "${tmpnewkey}" - ${WIPE[@]} "${tmpoldkey}" + ${=WIPE} "${tmpnewkey}" + ${=WIPE} "${tmpoldkey}" - notice "Your passphrase was successfully updated." + _success "Your passphrase was successfully updated." return 0 } +# }}} +# {{{ - List # list all tombs mounted in a readable format list_tombs() { if [ $1 ]; then @@ -1132,17 +1181,14 @@ list_tombs() { if ! [ $mounted_tombs ]; then if [ $1 ]; then - error "There seems to be no open tomb engraved as [${1}]" + die "There seems to be no open tomb engraved as [${1}]" else - error "I can't see any open tomb, may they all rest in peace." + die "I can't see any open tomb, may they all rest in peace." fi - exit 1 fi - for t in ${(f)mounted_tombs}; do mapper=`basename ${t[(ws:;:)1]}` - tombname=${t[(ws:;:)5]} tombmount=${t[(ws:;:)2]} tombfs=${t[(ws:;:)3]} @@ -1179,12 +1225,11 @@ list_tombs() { print -n "$fg_no_bold[white] free (" print -n "$fg_bold[white]$tombpercent" print "$fg_no_bold[white] full)" - + if [[ ${tombp} -ge 90 ]]; then print -n "$fg_no_bold[green]$tombname" print "$fg_bold[red] Your tomb is almost full!" fi - # now check hooks mtomb=`sed 's:\/:\\\/:g' <<< $tombmount` @@ -1198,7 +1243,8 @@ list_tombs() { done done } - +# }}} +# {{{ - Status launch_status() { # calculates the correct arguments to launch tomb-status tray # applet; it takes the tomb name as an argument and can be @@ -1206,13 +1252,13 @@ launch_status() { which tomb-status > /dev/null if [ $? != 0 ]; then - error "Cannot find tomb-status binary, operation aborted." + _warning "Cannot find tomb-status binary, operation aborted." return 1 fi if ! [ $DISPLAY ]; then - error "No active X display found, operation aborted." - error "Status launches a graphical tray applet, you need X running." + _warning "No active X display found, operation aborted." + _warning "Status launches a graphical tray applet, you need X running." return 1 fi @@ -1222,15 +1268,15 @@ launch_status() { tombs=`find /dev/mapper -name 'tomb.*'` how_many_tombs=`wc -w <<< "$tombs"` if [[ "$how_many_tombs" == "0" ]]; then - error "There is no open tomb, status cannot be launched" + _warning "There is no open tomb, status cannot be launched" return 1 elif [[ "$how_many_tombs" == "1" ]]; then - #mapper=`find /dev/mapper -name 'tomb.*'` + #mapper=`find /dev/mapper -name 'tomb.*'` tombname=`find /dev/mapper -name "tomb.*"` tombname=`basename $tombname | cut -d. -f2` - notice "launching status for tomb $tombname" + _success "launching status for tomb $tombname" else - error "Too many tombs mounted, please specify which one" + _warning "Too many tombs mounted, please specify which one" list_tombs return 0 fi @@ -1239,7 +1285,7 @@ launch_status() { tombname=$1 ls /dev/mapper | grep "^tomb.${tombname}.*" > /dev/null if [ $? != 0 ]; then - error "Cannot find any tomb named $tombname being open, operation aborted." + _warning "Cannot find any tomb named $tombname being open, operation aborted." return 1 fi fi @@ -1250,7 +1296,8 @@ launch_status() { tomb-status $tombmap $tombname $tombmount &! return 0 } - +# }}} +# {{{ - Install GUI # install mime-types, bells and whistles for the desktop # see http://developers.sun.com/solaris/articles/integrating_gnome.html # and freedesktop specs @@ -1258,7 +1305,7 @@ install_tomb() { # TODO: distro package deps (for binary) # debian: zsh, cryptsetup, sudo - act "updating mimetypes..." + _message "updating mimetypes..." cat <<EOF > /tmp/dyne-tomb.xml <?xml version="1.0"?> <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> @@ -1278,7 +1325,7 @@ EOF rm /tmp/dyne-tomb.xml - act "updating desktop..." + _message "updating desktop..." cat <<EOF > /usr/share/applications/tomb.desktop [Desktop Entry] Version=1.0 @@ -1294,9 +1341,9 @@ Categories=Utility;Security;Archiving;Filesystem; MimeType=application/x-tomb-volume; X-AppInstall-Package=tomb EOF - update-desktop-database + update-desktop-database - act "updating menus..." + _message "updating menus..." cat <<EOF > /etc/menu/tomb ?package(tomb):command="tomb" icon="/usr/share/pixmaps/monmort.xpm" needs="text" \ section="Applications/Accessories" title="Tomb" hints="Crypto" \ @@ -1304,7 +1351,7 @@ EOF EOF update-menus - act "updating mime info..." + _message "updating mime info..." cat <<EOF > /usr/share/mime-info/tomb.keys # actions for encrypted tomb storage application/x-tomb-volume: @@ -1326,7 +1373,7 @@ application/x-tomb-volume; tomb-open '%s'; priority=8 EOF update-mime - act "updating application entry..." + _message "updating application entry..." cat <<EOF > /usr/share/application-registry/tomb.applications tomb @@ -1337,9 +1384,13 @@ tomb requires_terminal=true mime-types=application/x-tomb-volume,application/x-tomb-key EOF - act "Tomb is now installed." + _message "Tomb is now installed." } +# }}} +# }}} +# {{{ OPTION PARSING +# {{{ - Check an option option_is_set() { #First argument, the option (something like "-s") #Second (optional) argument: if it's "out", command will print it out 'set'/'unset' @@ -1356,10 +1407,15 @@ option_is_set() { fi return $r; } +# }}} +# {{{ - Get an option value option_value() { #First argument, the option (something like "-s") <<< ${opts[$1]} } +# }}} +# }}} +# {{{ MAIN COMMAND main() { local -A subcommands_opts @@ -1394,6 +1450,8 @@ main() { subcommands_opts[mktemp]="" subcommands_opts[source]="" subcommands_opts[status]="" +# subcommands_opts[translate]="" + ### Detect subcommand local -aU every_opts #every_opts behave like a set; that is, an array with unique elements for optspec in $subcommands_opts$main_opts; do @@ -1409,107 +1467,135 @@ main() { if [[ -z $subcommand ]]; then subcommand="__default" fi - if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then #there's no such subcommand - error "Subcommand '$subcommand' doesn't exist" - exit 127 + + if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then + _warning "There's no such command \"$subcommand\"." + _failure "Please try -h for help" 127 +# die "Subcommand '$subcommand' doesn't exist" 127 fi argv=(${oldstar}) unset oldstar - ### Parsing global + command-specific options - # zsh magic: ${=string} will split to multiple arguments when spaces occur - set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]} - if [[ -n $cmd_opts ]]; then #if there is no option, we don't need parsing - zparseopts -M -E -D -Aopts ${cmd_opts} + ### Parsing global + command-specific options + # zsh magic: ${=string} will split to multiple arguments when spaces occur + set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]} + # if there is no option, we don't need parsing + if [[ -n $cmd_opts ]]; then + zparseopts -M -E -D -Aopts ${cmd_opts} if [[ $? != 0 ]]; then - error "Some error occurred during option processing. See \"tomb help\" for more info" - exit 127 + _warning "Some error occurred during option processing." + die "See \"tomb help\" for more info" 127 fi - fi - #build PARAM (array of arguments) and check if there are unrecognized options - ok=0 - PARAM=() - for arg in $*; do + fi + #build PARAM (array of arguments) and check if there are unrecognized options + ok=0 + PARAM=() + for arg in $*; do if [[ $arg == '--' || $arg == '-' ]]; then - ok=1 - continue #it shouldnt be appended to PARAM + ok=1 + continue #it shouldnt be appended to PARAM elif [[ $arg[1] == '-' ]]; then - if [[ $ok == 0 ]]; then - error "unrecognized option $arg" - exit 127 - fi + if [[ $ok == 0 ]]; then + die "unrecognized option $arg" 127 + fi fi PARAM+=$arg - done - #first parameter actually is the subcommand: delete it and shift - if [[ $subcommand != '__default' ]]; then - PARAM[1]=() - shift - fi - ### End parsing command-specific options + done + #first parameter actually is the subcommand: delete it and shift + if [[ $subcommand != '__default' ]]; then + PARAM[1]=() + shift + fi + ### End parsing command-specific options if ! option_is_set --no-color; then autoload colors; colors fi - ### Set global options (useless, but for code retro-compatibility) - for opt in ${(k)global_opts}; do + ### Set global options (useless, but for code retro-compatibility) + for opt in ${(k)global_opts}; do if [[ $opt == '-q' ]]; then - QUIET=1 + QUIET=1 elif [[ $opt == '-D' ]]; then - DEBUG=1 + DEBUG=1 fi - done + done - CMD=$subcommand - CMD2=$PARAM[1] - CMD3=$PARAM[2] + CMD=$subcommand + CMD2=$PARAM[1] + CMD3=$PARAM[2] - func "Tomb command: $CMD $CMD2 $CMD3" + xxx "Tomb command: $CMD $CMD2 $CMD3" case "$subcommand" in - create) check_priv ; create_tomb ;; - mount) check_priv ; mount_tomb ;; - open) check_priv ; mount_tomb ;; - umount) check_priv ; umount_tomb ${CMD2} ;; - close) check_priv ; umount_tomb ${CMD2} ;; - slam) check_priv ; SLAM=1; umount_tomb ${CMD2} ;; - passwd) check_priv ; change_passwd ${CMD2} ;; - list) list_tombs ${CMD2} ;; - status) launch_status ${CMD2} ;; - help) usage ;; - bury) if [ "$STEGHIDE" = 0 ]; then - error "steghide not installed. Cannot bury your key" - return 1 - fi - encode_key ${CMD2} ${CMD3} ;; - exhume) if [ "$STEGHIDE" = 0 ]; then - error "steghide not installed. Cannot exhume your key" - return 1 - fi - decode_key ${CMD2} ;; + create) + check_priv + create_tomb + ;; + mount|open) + check_priv + mount_tomb + ;; + umount|close|slam) + check_priv + [ "$subcommand" = "slam" ] && SLAM=1 + umount_tomb ${CMD2} + ;; + passwd) + check_priv + change_passwd ${CMD2} + ;; + list) + list_tombs ${CMD2} + ;; + status) + launch_status ${CMD2} + ;; + help) + usage + ;; + bury) + if [ "$STEGHIDE" = 0 ]; then + _warning "steghide not installed. Cannot bury your key" + return 1 + fi + encode_key ${CMD2} ${CMD3} + ;; + exhume) + if [ "$STEGHIDE" = 0 ]; then + _warning "steghide not installed. Cannot exhume your key" + return 1 + fi + decode_key ${CMD2} + ;; # internal commands useful to developers - 'source') return 0 ;; - install) check_priv ; install_tomb ;; - askpass) ask_password ${CMD2} ${CMD3} ;; - mktemp) safe_dir ${CMD2} ;; - translate) generate_translatable_strings ;; - __default) - if option_is_set -v; then - echo Tomb - $VERSION - else - usage - fi;; - *) error "command \"$CMD\" not recognized" - act "try -h for help" - return 1 - ;; + 'source') return 0 ;; + install) check_priv ; install_tomb ;; + askpass) ask_password ${CMD2} ${CMD3} ;; + mktemp) safe_dir ${CMD2} ;; + translate) generate_translatable_strings ;; + __default) + if option_is_set -v; then + echo Tomb - $VERSION + else + usage + fi + ;; + *) + _warning "command \"$CMD\" not recognized" + _message "try -h for help" + return 1 + ;; esac return $? } +# }}} +# {{{ RUNTIME check_bin main $@ if [[ $? != 0 ]]; then #this "if" seems useless, but avoid source tomb source from exiting exit $? fi +# }}} + diff --git a/src/undertaker b/src/undertaker @@ -6,7 +6,7 @@ # # Undertaker is a tool to retrieve tomb keys from various sources # -# Copyleft (C) 2011 Denis Roio <jaromil@dyne.org> +# {{{ Copyleft (C) 2011 Denis Roio <jaromil@dyne.org> # # This source code is free software; you can redistribute it and/or # modify it under the terms of the GNU Public License as published by @@ -22,6 +22,7 @@ # this source code; if not, write to: # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# }}} # first of all source the tomb core functions which tomb > /dev/null @@ -31,26 +32,24 @@ fi source tomb source if ! [ $1 ] ; then - error "an argument is missing, the undertaker is confused" - act "usage: undertaker url://host:path/to/tomb.key" - exit 1; + _warning "an argument is missing, the undertaker is confused" + _failure "usage: undertaker url://host:path/to/tomb.key" fi ARG1=${1} check_bin -notice "Undertaker will look for ${ARG1}" +_message "Undertaker will look for ${ARG1}" baseurl=${ARG1%//*} case $baseurl in bluetooth:) - act "access to bluetooth protocol requested" + _message "access to bluetooth protocol requested" which obexftp &> /dev/null if [[ $? != 0 ]]; then - error "obexftp not found, needed for bluetooth: operation aborted." - exit 1 + die "obexftp not found, needed for bluetooth: operation aborted." fi keytmp=`safe_dir undertaker` cd $keytmp @@ -58,18 +57,17 @@ case $baseurl in obexdevpath=${ARG1#*//} obexdev=${obexdevpath%%/*} obexpath=${obexdevpath#*/} - act "obex device: $obexdev" - act "obex path: $obexpath" + _message "obex device: $obexdev" + _message "obex path: $obexpath" obexftp -b $obexdev -g $obexpath if [[ $? != 0 ]]; then - error "a problem occurred retreiving the key via bluetooth." rmdir ${keytmp} - exit 1; + die "a problem occurred retreiving the key via bluetooth." fi # print out the key on stdout cat $obexpath >&1 # wipe out the key - ${WIPE[@]} $obexpath + ${=WIPE} $obexpath cd - rmdir ${keytmp} @@ -78,19 +76,18 @@ case $baseurl in ;; file:) - act "local file access requested" - error "TODO" + _message "local file access requested" + die "TODO" ;; http:) - act "access to web protocol requested" - error "TODO" + _message "access to web protocol requested" + die "TODO" ;; ssh:) - act "access to secure shell requested" - error "TODO" + _message "access to secure shell requested" + die "TODO" ;; *) - error "url protocol not recognized: $baseurl" - exit 1 + die "url protocol not recognized: $baseurl" ;; esac