ttomb - tomb - the crypto undertaker
 (HTM) git clone git://parazyd.org/tomb.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       ttomb (91675B)
       ---
            1 #!/data/data/com.termux/files/usr/bin/zsh
            2 #
            3 # Tomb, the Crypto Undertaker
            4 #
            5 # A commandline tool to easily operate encryption of secret data
            6 #
            7 
            8 # {{{ License
            9 
           10 # Copyright (C) 2007-2016 Dyne.org Foundation
           11 #
           12 # Tomb is designed, written and maintained by Denis Roio <jaromil@dyne.org>
           13 #
           14 # With contributions by Anathema, Boyska, Hellekin O. Wolf and GDrooid
           15 #
           16 # Gettext internationalization and Spanish translation is contributed by
           17 # GDrooid, French translation by Hellekin, Russian translation by fsLeg,
           18 # German translation by x3nu.
           19 #
           20 # Testing, reviews and documentation are contributed by Dreamer, Shining
           21 # the Translucent, Mancausoft, Asbesto Molesto, Nignux, Vlax, The Grugq,
           22 # Reiven, GDrooid, Alphazo, Brian May, TheJH, fsLeg, JoelMon and the
           23 # Linux Action Show!
           24 #
           25 # Tomb's artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
           26 #
           27 # Cryptsetup was developed by Christophe Saout and Clemens Fruhwirth.
           28 
           29 # This source code is free software; you can redistribute it and/or
           30 # modify it under the terms of the GNU Public License as published by
           31 # the Free Software Foundation; either version 3 of the License, or
           32 # (at your option) any later version.
           33 #
           34 # This source code is distributed in the hope that it will be useful,
           35 # but WITHOUT ANY WARRANTY; without even the implied warranty of
           36 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Please refer
           37 # to the GNU Public License for more details.
           38 #
           39 # You should have received a copy of the GNU Public License along with
           40 # this source code; if not, write to: Free Software Foundation, Inc.,
           41 # 675 Mass Ave, Cambridge, MA 02139, USA.
           42 
           43 # }}} - License
           44 
           45 # {{{ Global variables
           46 
           47 typeset VERSION="2.2-android"
           48 typeset DATE="Jan/2016"
           49 typeset TOMBEXEC=$0
           50 typeset TMPPREFIX=${TMPPREFIX:-/tmp}
           51 # TODO: configure which tmp dir to use from a cli flag
           52 
           53 # Tomb is using some global variables set by the shell:
           54 # TMPPREFIX, UID, GID, PATH, TTY, USERNAME
           55 # You can grep 'global variable' to see where they are used.
           56 
           57 # Keep a reference of the original command line arguments
           58 typeset -a OLDARGS
           59 for arg in "${(@)argv}"; do OLDARGS+=("$arg"); done
           60 
           61 # Special command requirements
           62 typeset -a DD WIPE PINENTRY
           63 DD=(dd)
           64 WIPE=(rm -f)
           65 PINENTRY=(pinentry)
           66 
           67 # load zsh regex module
           68 #zmodload zsh/regex
           69 #zmodload zsh/mapfile
           70 #zmodload -F zsh/stat b:zstat
           71 
           72 # make sure variables aren't exported
           73 unsetopt allexport
           74 
           75 # Flag optional commands if available (see _ensure_dependencies())
           76 typeset -i KDF=1
           77 typeset -i STEGHIDE=1
           78 typeset -i RESIZER=1
           79 typeset -i SWISH=1
           80 typeset -i QRENCODE=1
           81 
           82 # Default mount options
           83 typeset      MOUNTOPTS="rw,noatime,nodev"
           84 
           85 # Makes glob matching case insensitive
           86 unsetopt CASE_MATCH
           87 
           88 typeset -AH OPTS              # Command line options (see main())
           89 
           90 # Tomb context (see _plot())
           91 typeset -H TOMBPATH           # Full path to the tomb
           92 typeset -H TOMBDIR            # Directory where the tomb is
           93 typeset -H TOMBFILE           # File name of the tomb
           94 typeset -H TOMBNAME           # Name of the tomb
           95 
           96 # Tomb secrets
           97 typeset -H TOMBKEY            # Encrypted key contents (see forge_key(), recover_key())
           98 typeset -H TOMBKEYFILE        # Key file               (ditto)
           99 typeset -H TOMBSECRET         # Raw deciphered key     (see forge_key(), gpg_decrypt())
          100 typeset -H TOMBPASSWORD       # Raw tomb passphrase    (see gen_key(), ask_key_password())
          101 typeset -H TOMBTMP            # Filename of secure temp just created (see _tmp_create())
          102 
          103 typeset -aH TOMBTMPFILES      # Keep track of temporary files
          104 typeset -aH TOMBLOOPDEVS      # Keep track of used loop devices
          105 
          106 # Make sure sbin is in PATH (man zshparam)
          107 path+=( /sbin /usr/sbin )
          108 
          109 # For gettext
          110 export TEXTDOMAIN=tomb
          111 
          112 # }}}
          113 
          114 # {{{ Safety functions
          115 
          116 # Wrap sudo with a more visible message
          117 _sudo() { su -c "${@}" }
          118 
          119 # Cleanup anything sensitive before exiting.
          120 _endgame() {
          121 
          122     # Prepare some random material to overwrite vars
          123     local rr="$RANDOM"
          124     while [[ ${#rr} -lt 500 ]]; do
          125         rr+="$RANDOM"
          126     done
          127 
          128     # Ensure no information is left in unallocated memory
          129     TOMBPATH="$rr";      unset TOMBPATH
          130     TOMBDIR="$rr";       unset TOMBDIR
          131     TOMBFILE="$rr";      unset TOMBFILE
          132     TOMBNAME="$rr";      unset TOMBNAME
          133     TOMBKEY="$rr";       unset TOMBKEY
          134     TOMBKEYFILE="$rr";   unset TOMBKEYFILE
          135     TOMBSECRET="$rr";    unset TOMBSECRET
          136     TOMBPASSWORD="$rr";  unset TOMBPASSWORD
          137 
          138     # Clear temporary files
          139     for f in $TOMBTMPFILES; do
          140         ${=WIPE} "$f"
          141     done
          142     unset TOMBTMPFILES
          143 
          144     # Detach loop devices
          145     for l in $TOMBLOOPDEVS; do
          146         _sudo losetup -d "$l"
          147     done
          148     unset TOMBLOOPDEVS
          149 
          150 }
          151 
          152 # Trap functions for the _endgame event
          153 TRAPINT()  { _endgame INT   }
          154 TRAPEXIT() { _endgame EXIT  }
          155 TRAPHUP()  { _endgame HUP   }
          156 TRAPQUIT() { _endgame QUIT  }
          157 TRAPABRT() { _endgame ABORT }
          158 TRAPKILL() { _endgame KILL  }
          159 TRAPPIPE() { _endgame PIPE  }
          160 TRAPTERM() { _endgame TERM  }
          161 TRAPSTOP() { _endgame STOP  }
          162 
          163 _cat() { cat }
          164 
          165 _is_found() {
          166     # returns 0 if binary is found in path
          167     [[ "$1" = "" ]] && return 1
          168     command -v "$1" 1>/dev/null 2>/dev/null
          169     return $?
          170 }
          171 
          172 # Define sepulture's plot (setup tomb-related arguments)
          173 # Synopsis: _plot /path/to/the.tomb
          174 # Set TOMB{PATH,DIR,FILE,NAME}
          175 _plot() {
          176 
          177     # We set global variables
          178     typeset -g TOMBPATH TOMBDIR TOMBFILE TOMBNAME
          179 
          180     TOMBPATH="$1"
          181 
          182     TOMBDIR=$(dirname $TOMBPATH)
          183 
          184     TOMBFILE=$(basename $TOMBPATH)
          185 
          186     # The tomb name is TOMBFILE without an extension.
          187     # It can start with dots: ..foo.tomb -> ..foo
          188     TOMBNAME="${TOMBFILE%\.[^\.]*}"
          189     [[ -z $TOMBNAME ]] && {
          190         _failure "Tomb won't work without a TOMBNAME." }
          191 
          192 }
          193 
          194 # Provide a random filename in shared memory
          195 _tmp_create() {
          196     [[ -d "$TMPPREFIX" ]] || {
          197         # we create the tempdir with the sticky bit on
          198         _sudo mkdir -m 1777 "$TMPPREFIX"
          199         [[ $? == 0 ]] || _failure "Fatal error creating the temporary directory: ::1 temp dir::" "$TMPPREFIX"
          200     }
          201 
          202     # We're going to add one more $RANDOM for each time someone complains
          203     # about this being too weak of a random.
          204     tfile="${TMPPREFIX}/$RANDOM$RANDOM$RANDOM$RANDOM"   # Temporary file
          205     umask 066
          206     [[ $? == 0 ]] || {
          207         _failure "Fatal error setting the permission umask for temporary files" }
          208 
          209     [[ -r "$tfile" ]] && {
          210         _failure "Someone is messing up with us trying to hijack temporary files." }
          211 
          212     touch "$tfile"
          213     [[ $? == 0 ]] || {
          214         _failure "Fatal error creating a temporary file: ::1 temp file::" "$tfile" }
          215 
          216     _verbose "Created tempfile: ::1 temp file::" "$tfile"
          217     TOMBTMP="$tfile"
          218     TOMBTMPFILES+=("$tfile")
          219 
          220     return 0
          221 }
          222 
          223 # Check if a *block* device is encrypted
          224 # Synopsis: _is_encrypted_block /path/to/block/device
          225 # Return 0 if it is an encrypted block device
          226 _is_encrypted_block() {
          227     local    b=$1 # Path to a block device
          228     local    s="" # lsblk option -s (if available)
          229 
          230     # Issue #163
          231     # lsblk --inverse appeared in util-linux 2.22
          232     # but --version is not consistent...
          233     lsblk --help | grep -Fq -- --inverse
          234     [[ $? -eq 0 ]] && s="--inverse"
          235 
          236     sudo lsblk $s -o type -n $b 2>/dev/null \
          237         | egrep -q '^crypt$'
          238 
          239     return $?
          240 }
          241 
          242 # Check if swap is activated
          243 # Return 0 if NO swap is used, 1 if swap is used.
          244 # Return 1 if any of the swaps is not encrypted.
          245 # Return 2 if swap(s) is(are) used, but ALL encrypted.
          246 # Use _check_swap in functions. It will call this function and
          247 # exit if unsafe swap is present.
          248 _ensure_safe_swap() {
          249 
          250     local -i r=1    # Return code: 0 no swap, 1 unsafe swap, 2 encrypted
          251     local -a swaps  # List of swap partitions
          252     local    bone is_crypt
          253 
          254     swaps="$(awk '/^\// { print $1 }' /proc/swaps 2>/dev/null)"
          255     [[ -z "$swaps" ]] && return 0 # No swap partition is active
          256 
          257     _message "An active swap partition is detected..."
          258     for s in $=swaps; do
          259         { _is_encrypted_block $s } && { r=2 } || {
          260             # We're dealing with unencrypted stuff.
          261             # Maybe it lives on an encrypted filesystem anyway.
          262             # @todo: verify it's actually on an encrypted FS (see #163 and !189)
          263             # Well, no: bail out.
          264             r=1; break
          265         }
          266     done
          267 
          268     if [[ $r -eq 2 ]]; then
          269         _success "All your swaps are belong to crypt. Good."
          270     else
          271         _warning "This poses a security risk."
          272         _warning "You can deactivate all swap partitions using the command:"
          273         _warning " swapoff -a"
          274         _warning "[#163] I may not detect plain swaps on an encrypted volume."
          275         _warning "But if you want to proceed like this, use the -f (force) flag."
          276     fi
          277     return $r
          278 
          279 }
          280 
          281 # Wrapper to allow encrypted swap and remind the user about possible
          282 # data leaks to disk if swap is on, which shouldn't be ignored. It could
          283 # be run once in main(), but as swap evolves, it's better to run it
          284 # whenever swap may be needed.
          285 # Exit if unencrypted swap is active on the system.
          286 _check_swap() {
          287     if ! option_is_set -f && ! option_is_set --ignore-swap; then
          288         _ensure_safe_swap
          289         case $? in
          290             0|2)     # No, or encrypted swap
          291                 return 0
          292                 ;;
          293             *)       # Unencrypted swap
          294                 _failure "Operation aborted."
          295                 ;;
          296         esac
          297     fi
          298 }
          299 
          300 # Ask user for a password
          301 # Wraps around the pinentry command, from the GnuPG project, as it
          302 # provides better security and conveniently use the right toolkit.
          303 ask_password() {
          304 
          305     local description="$1"
          306     local title="${2:-Enter tomb password.}"
          307     local output
          308     local password
          309     local gtkrc
          310     local theme
          311 
          312     # Distributions have broken wrappers for pinentry: they do
          313     # implement fallback, but they disrupt the output somehow.  We are
          314     # better off relying on less intermediaries, so we implement our
          315     # own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4
          316     # and x11.
          317 
          318     # make sure LANG is set, default to C
          319     LANG=${LANG:-C}
          320 
          321     _verbose "asking password with tty=$TTY lc-ctype=$LANG"
          322 
          323     if [[ "$DISPLAY" = "" ]]; then
          324 
          325         if _is_found "pinentry-curses"; then
          326             _verbose "using pinentry-curses"
          327             output=`cat <<EOF | pinentry-curses
          328 OPTION ttyname=$TTY
          329 OPTION lc-ctype=$LANG
          330 SETTITLE $title
          331 SETDESC $description
          332 SETPROMPT Password:
          333 GETPIN
          334 EOF`
          335         else
          336             _failure "Cannot find pinentry-curses and no DISPLAY detected."
          337         fi
          338 
          339     else # a DISPLAY is found to be active
          340 
          341         # customized gtk2 dialog with a skull (if extras are installed)
          342         if _is_found "pinentry-gtk-2"; then
          343             _verbose "using pinentry-gtk2"
          344 
          345             gtkrc=""
          346             theme=/share/themes/tomb/gtk-2.0-key/gtkrc
          347             for i in /usr/local /usr; do
          348                 [[ -r $i/$theme ]] && {
          349                     gtkrc="$i/$theme"
          350                     break
          351                 }
          352             done
          353             [[ "$gtkrc" = "" ]] || {
          354                 gtkrc_old="$GTK2_RC_FILES"
          355                 export GTK2_RC_FILES="$gtkrc"
          356             }
          357             output=`cat <<EOF | pinentry-gtk-2
          358 OPTION ttyname=$TTY
          359 OPTION lc-ctype=$LANG
          360 SETTITLE $title
          361 SETDESC $description
          362 SETPROMPT Password:
          363 GETPIN
          364 EOF`
          365             [[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old"
          366 
          367             # TODO QT4 customization of dialog
          368         elif _is_found "pinentry-qt4"; then
          369             _verbose "using pinentry-qt4"
          370 
          371             output=`cat <<EOF | pinentry-qt4
          372 OPTION ttyname=$TTY
          373 OPTION lc-ctype=$LANG
          374 SETTITLE $title
          375 SETDESC $description
          376 SETPROMPT Password:
          377 GETPIN
          378 EOF`
          379 
          380             # TODO X11 customization of dialog
          381         elif _is_found "pinentry-x11"; then
          382             _verbose "using pinentry-x11"
          383 
          384             output=`cat <<EOF | pinentry-x11
          385 OPTION ttyname=$TTY
          386 OPTION lc-ctype=$LANG
          387 SETTITLE $title
          388 SETDESC $description
          389 SETPROMPT Password:
          390 GETPIN
          391 EOF`
          392 
          393         else
          394 
          395             if _is_found "pinentry-curses"; then
          396                 _verbose "using pinentry-curses"
          397 
          398                 _warning "Detected DISPLAY, but only pinentry-curses is found."
          399                 output=`cat <<EOF | pinentry-curses
          400 OPTION ttyname=$TTY
          401 OPTION lc-ctype=$LANG
          402 SETTITLE $title
          403 SETDESC $description
          404 SETPROMPT Password:
          405 GETPIN
          406 EOF`
          407             else
          408                 _failure "Cannot find any pinentry: impossible to ask for password."
          409             fi
          410 
          411         fi
          412 
          413     fi # end of DISPLAY block
          414 
          415     # parse the pinentry output
          416     for i in ${(f)output}; do
          417         print "$i" | grep "^ERR.*" && {
          418             _warning "Pinentry error: ::1 error::" ${i[(w)3]}
          419             print "canceled"
          420             return 1 }
          421 
          422         # here the password is found
          423         print "$i" | grep "^D .*" && password="${i##D }"
          424     done
          425 
          426     [[ "$password" = "" ]] && {
          427         _warning "Empty password"
          428         print "empty"
          429         return 1 }
          430 
          431     print "$password"
          432     return 0
          433 }
          434 
          435 # Android hasn't real mtab, we maintain our own for tombs
          436 mount_list() { [[ -r $HOME/.tomb/mtab ]] && cat $HOME/.tomb/mtab }
          437 mount_add_tomb_mtab() {
          438     mkdir -p $HOME/.tomb
          439     touch $HOME/.tomb/mtab
          440     print "$1;$2;ext2;$MOUNTOPTS;[$TOMBNAME]" >> $HOME/.tomb/mtab
          441 }
          442 
          443 # Check if a filename is a valid tomb
          444 is_valid_tomb() {
          445     _verbose "is_valid_tomb ::1 tomb file::" $1
          446 
          447     # First argument must be the path to a tomb
          448     [[ -z "$1" ]] && {
          449         _failure "Tomb file is missing from arguments." }
          450 
          451     _fail=0
          452     # Tomb file must be a readable, writable, non-empty regular file.
          453     [[ ! -w "$1" ]] && {
          454         _warning "Tomb file is not writable: ::1 tomb file::" $1
          455         _fail=1
          456     }
          457     [[ ! -f "$1" ]] && {
          458         _warning "Tomb file is not a regular file: ::1 tomb file::" $1
          459         _fail=1
          460     }
          461     [[ ! -s "$1" ]] && {
          462         _warning "Tomb file is empty (zero length): ::1 tomb file::" $1
          463         _fail=1
          464     }
          465 
          466     [[ $_fail = 1 ]] && {
          467         _failure "Tomb command failed: ::1 command name::" $subcommand
          468     }
          469 
          470     # TODO: split the rest of that function out.
          471     # We already have a valid tomb, now we're checking
          472     # whether we can alter it.
          473 
          474     # Tomb file may be a LUKS FS (or we are creating it)
          475     file $1 | grep "luks encrypted file" || {
          476         _warning "File is not yet a tomb: ::1 tomb file::" $1 }
          477 
          478     _plot $1     # Set TOMB{PATH,DIR,FILE,NAME}
          479 
          480     # Tomb already mounted (or we cannot alter it)
          481     mount_list | grep "${TOMBFILE}.*\[$TOMBNAME\]$" && {
          482         _failure "Tomb is currently in use: ::1 tomb name::" $TOMBNAME
          483     }
          484 
          485     _message "Valid tomb file found: ::1 tomb path::" $TOMBPATH
          486 
          487     return 0
          488 }
          489 
          490 # $1 is the tomb file to be lomounted
          491 lo_mount() {
          492     tpath="$1"
          493 
          494     # TODO: Android always reports loop0 as next
          495     #       we need to implement our own loop table
          496 
          497     # check if we have support for loop mounting
          498     _nstloop=`losetup -f`
          499     [[ $? = 0 ]] || {
          500         _warning "Loop mount of volumes is not possible on this machine, this error"
          501         _warning "often occurs on VPS and kernels that don't provide the loop module."
          502         _warning "It is impossible to use Tomb on this machine at this conditions."
          503         _failure "Operation aborted."
          504     }
          505 
          506     _sudo losetup -f "$tpath" # allocates the next loopback for our file
          507 
          508     TOMBLOOPDEVS+=("$_nstloop") # add to array of lodevs used
          509 
          510     return 0
          511 }
          512 
          513 # print out latest loopback mounted
          514 lo_new() { print - "${TOMBLOOPDEVS[${#TOMBLOOPDEVS}]}" }
          515 
          516 # $1 is the path to the lodev to be preserved after quit
          517 lo_preserve() {
          518     _verbose "lo_preserve on ::1 path::" $1
          519     # remove the lodev from the tomb_lodevs array
          520     TOMBLOOPDEVS=("${(@)TOMBLOOPDEVS:#$1}")
          521 }
          522 
          523 # eventually used for debugging
          524 dump_secrets() {
          525     print "TOMBPATH: $TOMBPATH"
          526     print "TOMBNAME: $TOMBNAME"
          527 
          528     print "TOMBKEY len: ${#TOMBKEY}"
          529     print "TOMBKEYFILE: $TOMBKEYFILE"
          530     print "TOMBSECRET len: ${#TOMBSECRET}"
          531     print "TOMBPASSWORD: $TOMBPASSWORD"
          532 
          533     print "TOMBTMPFILES: ${(@)TOMBTMPFILES}"
          534     print "TOMBLOOPDEVS: ${(@)TOMBLOOPDEVS}"
          535 }
          536 
          537 # }}}
          538 
          539 # {{{ Commandline interaction
          540 
          541 usage() {
          542     _print "Syntax: tomb [options] command [arguments]"
          543     _print "\000"
          544     _print "Commands:"
          545     _print "\000"
          546     _print " // Creation:"
          547     _print " dig     create a new empty TOMB file of size -s in MiB"
          548     _print " forge   create a new KEY file and set its password"
          549     _print " lock    installs a lock on a TOMB to use it with KEY"
          550     _print "\000"
          551     _print " // Operations on tombs:"
          552     _print " open    open an existing TOMB (-k KEY file or - for stdin)"
          553     _print " index   update the search indexes of tombs"
          554     _print " search  looks for filenames matching text patterns"
          555     _print " list    list of open TOMBs and information on them"
          556     _print " close   close a specific TOMB (or 'all')"
          557     _print " slam    slam a TOMB killing all programs using it"
          558     [[ $RESIZER == 1 ]] && {
          559         _print " resize  resize a TOMB to a new size -s (can only grow)"
          560     }
          561     _print "\000"
          562     _print " // Operations on keys:"
          563     _print " passwd  change the password of a KEY (needs old pass)"
          564     _print " setkey  change the KEY locking a TOMB (needs old key and pass)"
          565     _print "\000"
          566     [[ $QRENCODE == 1 ]] && {
          567         _print " // Backup on paper:"
          568         _print " engrave makes a QR code of a KEY to be saved on paper"
          569     }
          570     _print "\000"
          571     [[ $STEGHIDE == 1 ]] && {
          572         _print " // Steganography:"
          573         _print " bury    hide a KEY inside a JPEG image (for use with -k)"
          574         _print " exhume  extract a KEY from a JPEG image (prints to stdout)"
          575     }
          576     _print "\000"
          577     _print "Options:"
          578     _print "\000"
          579     _print " -s     size of the tomb file when creating/resizing one (in MiB)"
          580     _print " -k     path to the key to be used ('-k -' to read from stdin)"
          581     _print " -n     don't process the hooks found in tomb"
          582     _print " -o     options passed to commands: open, lock, forge (see man)"
          583     _print " -f     force operation (i.e. even if swap is active)"
          584     [[ $KDF == 1 ]] && {
          585         _print " --kdf  forge keys armored against dictionary attacks"
          586     }
          587 
          588     _print "\000"
          589     _print " -h     print this help"
          590     _print " -v     print version, license and list of available ciphers"
          591     _print " -q     run quietly without printing informations"
          592     _print " -D     print debugging information at runtime"
          593     _print "\000"
          594     _print "For more informations on Tomb read the manual: man tomb"
          595     _print "Please report bugs on <http://github.com/dyne/tomb/issues>."
          596 }
          597 
          598 
          599 # Check whether a commandline option is set.
          600 #
          601 # Synopsis: option_is_set -flag [out]
          602 #
          603 # First argument is the commandline flag (e.g., "-s").
          604 # If the second argument is present and set to 'out', print out the
          605 # result: either 'set' or 'unset' (useful for if conditions).
          606 #
          607 # Return 0 if is set, 1 otherwise
          608 option_is_set() {
          609     local -i r   # the return code (0 = set, 1 = unset)
          610 
          611     [[ -n ${(k)OPTS[$1]} ]];
          612     r=$?
          613 
          614     [[ $2 == "out" ]] && {
          615         [[ $r == 0 ]] && { print 'set' } || { print 'unset' }
          616     }
          617 
          618     return $r;
          619 }
          620 
          621 # Print the option value matching the given flag
          622 # Unique argument is the commandline flag (e.g., "-s").
          623 option_value() {
          624     print -n - "${OPTS[$1]}"
          625 }
          626 
          627 # Messaging function with pretty coloring
          628 function _msg() {
          629     local msg="$2"
          630     command -v gettext 1>/dev/null 2>/dev/null && msg="$(gettext -s "$2")"
          631     for i in $(seq 3 ${#});
          632     do
          633         msg=${(S)msg//::$(($i - 2))*::/$*[$i]}
          634     done
          635 
          636     local command="print -P"
          637     local progname="$fg[magenta]${TOMBEXEC##*/}$reset_color"
          638     local message="$fg_bold[normal]$fg_no_bold[normal]$msg$reset_color"
          639     local -i returncode
          640 
          641     case "$1" in
          642         inline)
          643             command+=" -n"; pchars=" > "; pcolor="yellow"
          644             ;;
          645         message)
          646             pchars=" . "; pcolor="white"; message="$fg_no_bold[$pcolor]$msg$reset_color"
          647             ;;
          648         verbose)
          649             pchars="[D]"; pcolor="blue"
          650             ;;
          651         success)
          652             pchars="(*)"; pcolor="green"; message="$fg_no_bold[$pcolor]$msg$reset_color"
          653             ;;
          654         warning)
          655             pchars="[W]"; pcolor="yellow"; message="$fg_no_bold[$pcolor]$msg$reset_color"
          656             ;;
          657         failure)
          658             pchars="[E]"; pcolor="red"; message="$fg_no_bold[$pcolor]$msg$reset_color"
          659             returncode=1
          660             ;;
          661         print)
          662             progname=""
          663             ;;
          664         *)
          665             pchars="[F]"; pcolor="red"
          666             message="Developer oops!  Usage: _msg MESSAGE_TYPE \"MESSAGE_CONTENT\""
          667             returncode=127
          668             ;;
          669     esac
          670     ${=command} "${progname} $fg_bold[$pcolor]$pchars$reset_color ${message}$color[reset_color]" >&2
          671     return $returncode
          672 }
          673 
          674 function _message say() {
          675     local notice="message"
          676     [[ "$1" = "-n" ]] && shift && notice="inline"
          677     option_is_set -q || _msg "$notice" $@
          678     return 0
          679 }
          680 
          681 function _verbose xxx() {
          682     option_is_set -D && _msg verbose $@
          683     return 0
          684 }
          685 
          686 function _success yes() {
          687     option_is_set -q || _msg success $@
          688     return 0
          689 }
          690 
          691 function _warning  no() {
          692     option_is_set -q || _msg warning $@
          693     return 1
          694 }
          695 
          696 function _failure die() {
          697     typeset -i exitcode=${exitv:-1}
          698     option_is_set -q || _msg failure $@
          699     # be sure we forget the secrets we were told
          700     exit $exitcode
          701 }
          702 
          703 function _print() {
          704     option_is_set -q || _msg print $@
          705     return 0
          706 }
          707 
          708 _list_optional_tools() {
          709     typeset -a _deps
          710     _deps=(gettext dcfldd wipe steghide)
          711     _deps+=(resize2fs tomb-kdb-pbkdf2 qrencode swish-e unoconv)
          712     for d in $_deps; do
          713         _print "`which $d`"
          714     done
          715     return 0
          716 }
          717 
          718 
          719 # Check program dependencies
          720 #
          721 # Tomb depends on system utilities that must be present, and other
          722 # functionality that can be provided by various programs according to
          723 # what's available on the system.  If some required commands are
          724 # missing, bail out.
          725 _ensure_dependencies() {
          726 
          727     # Check for required programs
          728     for req in pinentry gpg mkfs.ext2 e2fsck; do
          729         command -v $req 1>/dev/null 2>/dev/null || {
          730             _failure "Missing required dependency ::1 command::.  Please install it." $req }
          731     done
          732 
          733     # Ensure system binaries are available in the PATH
          734     path+=(/sbin /usr/sbin /system/xbin) # zsh magic
          735 
          736     # Which dd command to use
          737     command -v dcfldd 1>/dev/null 2>/dev/null && DD=(dcfldd statusinterval=1)
          738 
          739     # Which wipe command to use
          740     command -v wipe 1>/dev/null 2>/dev/null && WIPE=(wipe -f -s)
          741 
          742     # Check for steghide
          743     command -v steghide 1>/dev/null 2>/dev/null || STEGHIDE=0
          744     # Check for resize
          745     command -v resize2fs 1>/dev/null 2>/dev/null || RESIZER=0
          746     # Check for KDF auxiliary tools
          747     command -v tomb-kdb-pbkdf2 1>/dev/null 2>/dev/null || KDF=0
          748     # Check for Swish-E file content indexer
          749     command -v swish-e 1>/dev/null 2>/dev/null || SWISH=0
          750     # Check for QREncode for paper backups of keys
          751     command -v qrencode 1>/dev/null 2>/dev/null || QRENCODE=0
          752 }
          753 
          754 # }}} - Commandline interaction
          755 
          756 # {{{ Key operations
          757 
          758 # $1 is the encrypted key contents we are checking
          759 is_valid_key() {
          760     local key="$1"       # Unique argument is an encrypted key to test
          761 
          762     _verbose "is_valid_key"
          763 
          764     [[ -z $key ]] && key=$TOMBKEY
          765     [[ "$key" = "cleartext" ]] && {
          766         { option_is_set --unsafe } || {
          767             _warning "cleartext key from stdin selected: this is unsafe."
          768             exitv=127 _failure "please use --unsafe if you really want to do this."
          769         }
          770         _warning "received key in cleartext from stdin (unsafe mode)"
          771         return 0 }
          772 
          773     [[ -z $key ]] && {
          774         _warning "is_valid_key() called without an argument."
          775         return 1
          776     }
          777 
          778     # If the key file is an image don't check file header
          779     [[ -r $TOMBKEYFILE ]] \
          780         && file $TOMBKEYFILE | grep "JP.G" \
          781         && {
          782         _message "Key is an image, it might be valid."
          783         return 0 }
          784 
          785     print $key | grep "BEGIN PGP" && {
          786         _message "Key is valid."
          787         return 0 }
          788 
          789     return 1
          790 }
          791 
          792 # $1 is a string containing an encrypted key
          793 _tomb_key_recover recover_key() {
          794     local key="${1}"    # Unique argument is an encrypted key
          795 
          796     _warning "Attempting key recovery."
          797 
          798     _head="${key[(f)1]}" # take the first line
          799 
          800     TOMBKEY=""        # Reset global variable
          801 
          802     [[ $_head =~ "^_KDF_" ]] && TOMBKEY+="$_head\n"
          803 
          804     TOMBKEY+="-----BEGIN PGP MESSAGE-----\n"
          805     TOMBKEY+="$key\n"
          806     TOMBKEY+="-----END PGP MESSAGE-----\n"
          807 
          808     return 0
          809 }
          810 
          811 # Retrieve the tomb key from the file specified from the command line,
          812 # or from stdin if -k - was selected.  Run validity checks on the
          813 # file.  On success, return 0 and print out the full path of the key.
          814 # Set global variables TOMBKEY and TOMBKEYFILE.
          815 _load_key() {
          816     local keyfile="$1"    # Unique argument is an optional keyfile
          817 
          818     [[ -z $keyfile ]] && keyfile=$(option_value -k)
          819     [[ -z $keyfile ]] && {
          820         _failure "This operation requires a key file to be specified using the -k option." }
          821 
          822     if [[ $keyfile == "-" ]]; then
          823         _verbose "load_key reading from stdin."
          824         _message "Waiting for the key to be piped from stdin... "
          825         TOMBKEYFILE=stdin
          826         TOMBKEY=$(cat)
          827     elif [[ $keyfile == "cleartext" ]]; then
          828         _verbose "load_key reading SECRET from stdin"
          829         _message "Waiting for the key to be piped from stdin... "
          830         TOMBKEYFILE=cleartext
          831         TOMBKEY=cleartext
          832         TOMBSECRET=$(cat)
          833     else
          834         _verbose "load_key argument: ::1 key file::" $keyfile
          835         [[ -r $keyfile ]] || _failure "Key not found, specify one using -k."
          836         TOMBKEYFILE=$keyfile
          837         TOMBKEY="`cat $TOMBKEYFILE`"
          838     fi
          839 
          840     _verbose "load_key: ::1 key::" $TOMBKEYFILE
          841 
          842     [[ "$TOMBKEY" = "" ]] && {
          843         # something went wrong, there is no key to load
          844         # this occurs especially when piping from stdin and aborted
          845         _failure "Key not found, specify one using -k."
          846     }
          847 
          848     is_valid_key $TOMBKEY || {
          849         _warning "The key seems invalid or its format is not known by this version of Tomb."
          850         _tomb_key_recover $TOMBKEY
          851     }
          852 
          853     # Declared TOMBKEYFILE (path)
          854     # Declared TOMBKEY (contents)
          855 
          856     return 0
          857 }
          858 
          859 # takes two args just like get_lukskey
          860 # prints out the decrypted content
          861 # contains tweaks for different gpg versions
          862 gpg_decrypt() {
          863     # fix for gpg 1.4.11 where the --status-* options don't work ;^/
          864     local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}')
          865     local gpgpass="$1\n$TOMBKEY"
          866     local gpgstatus
          867 
          868     [[ $gpgver == "1.4.11" ]] && {
          869         _verbose "GnuPG is version 1.4.11 - adopting status fix."
          870 
          871         TOMBSECRET=`print - "$gpgpass" | \
          872             gpg --batch --passphrase-fd 0 --no-tty --no-options`
          873         ret=$?
          874         unset gpgpass
          875 
          876     } || { # using status-file in gpg != 1.4.11
          877 
          878         TOMBSECRET=`print - "$gpgpass" | \
          879             gpg --batch --passphrase-fd 0 --no-tty --no-options \
          880             --status-fd 2 --no-mdc-warning --no-permission-warning \
          881             --no-secmem-warning` |& grep GNUPG: \
          882             | read -r -d'\n' gpgstatus
          883 
          884         unset gpgpass
          885 
          886         ret=1
          887 
          888         print - "${gpgstatus}" | grep "DECRYPTION_OKAY" && { ret=0 }
          889 
          890 
          891     }
          892     return $ret
          893 
          894 }
          895 
          896 
          897 # Gets a key file and a password, prints out the decoded contents to
          898 # be used directly by Luks as a cryptographic key
          899 get_lukskey() {
          900     # $1 is the password
          901     _verbose "get_lukskey"
          902 
          903     _password="$1"
          904 
          905 
          906     firstline="${TOMBKEY[(f)1]}"
          907 
          908     # key is KDF encoded
          909     if print - "$firstline" | grep '^_KDF_'; then
          910         kdf_hash="${firstline[(ws:_:)2]}"
          911         _verbose "KDF: ::1 kdf::" "$kdf_hash"
          912         case "$kdf_hash" in
          913             "pbkdf2sha1")
          914                 kdf_salt="${firstline[(ws:_:)3]}"
          915                 kdf_ic="${firstline[(ws:_:)4]}"
          916                 kdf_len="${firstline[(ws:_:)5]}"
          917                 _message "Unlocking KDF key protection ($kdf_hash)"
          918                 _verbose "KDF salt: $kdf_salt"
          919                 _verbose "KDF ic: $kdf_ic"
          920                 _verbose "KDF len: $kdf_len"
          921                 _password=$(tomb-kdb-pbkdf2 $kdf_salt $kdf_ic $kdf_len 2>/dev/null <<<$_password)
          922                 ;;
          923             *)
          924                 _failure "No suitable program for KDF ::1 program::." $pbkdf_hash
          925                 unset _password
          926                 return 1
          927                 ;;
          928         esac
          929 
          930         # key needs to be exhumed from an image
          931         # TODO
          932 #    elif [[ -r $TOMBKEYFILE && print $(file $TOMBKEYFILE) |grep "JP.G" ]]; then
          933 
          934 #       exhume_key $TOMBKEYFILE "$_password"
          935 
          936     fi
          937 
          938     gpg_decrypt "$_password" # Save decrypted contents into $TOMBSECRET
          939 
          940     ret="$?"
          941 
          942     _verbose "get_lukskey returns ::1::" $ret
          943     return $ret
          944 }
          945 
          946 # This function asks the user for the password to use the key it tests
          947 # it against the return code of gpg on success returns 0 and saves
          948 # the password in the global variable $TOMBPASSWORD
          949 ask_key_password() {
          950     [[ -z "$TOMBKEYFILE" ]] && {
          951         _failure "Internal error: ask_key_password() called before _load_key()." }
          952 
          953     [[ "$TOMBKEYFILE" = "cleartext" ]] && {
          954         _verbose "no password needed, using secret bytes from stdin"
          955         return 0 }
          956 
          957     _message "A password is required to use key ::1 key::" $TOMBKEYFILE
          958     passok=0
          959     tombpass=""
          960     if [[ "$1" = "" ]]; then
          961 
          962         for c in 1 2 3; do
          963             if [[ $c == 1 ]]; then
          964                 tombpass=$(ask_password "Insert password to: $TOMBKEYFILE")
          965             else
          966                 tombpass=$(ask_password "Insert password to: $TOMBKEYFILE (attempt $c)")
          967             fi
          968             [[ $? = 0 ]] || {
          969                 _warning "User aborted password dialog."
          970                 return 1
          971             }
          972 
          973             get_lukskey "$tombpass"
          974 
          975             [[ $? = 0 ]] && {
          976                 passok=1; _message "Password OK."
          977                 break;
          978             }
          979         done
          980 
          981     else
          982         # if a second argument is present then the password is already known
          983         tombpass="$1"
          984         _verbose "ask_key_password with tombpass: ::1 tomb pass::" $tombpass
          985 
          986         get_lukskey "$tombpass"
          987 
          988         [[ $? = 0 ]] && {
          989             passok=1; _message "Password OK."
          990         }
          991 
          992     fi
          993     [[ $passok == 1 ]] || return 1
          994 
          995     TOMBPASSWORD=$tombpass
          996     return 0
          997 }
          998 
          999 # call cryptsetup with arguments using the currently known secret
         1000 # echo flags eliminate newline and disable escape (BSD_ECHO)
         1001 _cryptsetup() {
         1002     print -R -n - "$TOMBSECRET" | _sudo cryptsetup --key-file - ${=@}
         1003     return $?
         1004 }
         1005 
         1006 # change tomb key password
         1007 change_passwd() {
         1008     local tmpnewkey lukskey c tombpass tombpasstmp
         1009 
         1010     _check_swap  # Ensure swap is secure, if any
         1011     _load_key    # Try loading key from option -k and set TOMBKEYFILE
         1012 
         1013     _message "Commanded to change password for tomb key ::1 key::" $TOMBKEYFILE
         1014 
         1015     _tmp_create
         1016     tmpnewkey=$TOMBTMP
         1017 
         1018     if option_is_set --tomb-old-pwd; then
         1019         local tomboldpwd="`option_value --tomb-old-pwd`"
         1020         _verbose "tomb-old-pwd = ::1 old pass::" $tomboldpwd
         1021         ask_key_password "$tomboldpwd"
         1022     else
         1023         ask_key_password
         1024     fi
         1025     [[ $? == 0 ]] || _failure "No valid password supplied."
         1026 
         1027     _success "Changing password for ::1 key file::" $TOMBKEYFILE
         1028 
         1029     # Here $TOMBSECRET contains the key material in clear
         1030 
         1031     { option_is_set --tomb-pwd } && {
         1032         local tombpwd="`option_value --tomb-pwd`"
         1033         _verbose "tomb-pwd = ::1 new pass::" $tombpwd
         1034         gen_key "$tombpwd" >> "$tmpnewkey"
         1035     } || {
         1036         gen_key >> "$tmpnewkey"
         1037     }
         1038 
         1039     { is_valid_key "`cat $tmpnewkey`" } || {
         1040         _failure "Error: the newly generated keyfile does not seem valid." }
         1041 
         1042     # Copy the new key as the original keyfile name
         1043     cp -f "${tmpnewkey}" $TOMBKEYFILE
         1044     _success "Your passphrase was successfully updated."
         1045 
         1046     return 0
         1047 }
         1048 
         1049 
         1050 # takes care to encrypt a key
         1051 # honored options: --kdf  --tomb-pwd -o
         1052 gen_key() {
         1053     # $1 the password to use; if not set ask user
         1054     # -o is the --cipher-algo to use (string taken by GnuPG)
         1055     local algopt="`option_value -o`"
         1056     local algo="${algopt:-AES256}"
         1057     # here user is prompted for key password
         1058     tombpass=""
         1059     tombpasstmp=""
         1060 
         1061     if [ "$1" = "" ]; then
         1062         while true; do
         1063             # 3 tries to write two times a matching password
         1064             tombpass=`ask_password "Type the new password to secure your key"`
         1065             if [[ $? != 0 ]]; then
         1066                 _failure "User aborted."
         1067             fi
         1068             if [ -z $tombpass ]; then
         1069                 _failure "You set empty password, which is not possible."
         1070             fi
         1071             tombpasstmp=$tombpass
         1072             tombpass=`ask_password "Type the new password to secure your key (again)"`
         1073             if [[ $? != 0 ]]; then
         1074                 _failure "User aborted."
         1075             fi
         1076             if [ "$tombpasstmp" = "$tombpass" ]; then
         1077                 break;
         1078             fi
         1079             unset tombpasstmp
         1080             unset tombpass
         1081         done
         1082     else
         1083         tombpass="$1"
         1084         _verbose "gen_key takes tombpass from CLI argument: ::1 tomb pass::" $tombpass
         1085     fi
         1086 
         1087     header=""
         1088     [[ $KDF == 1 ]] && {
         1089         { option_is_set --kdf } && {
         1090             # KDF is a new key strenghtening technique against brute forcing
         1091             # see: https://github.com/dyne/Tomb/issues/82
         1092             itertime="`option_value --kdf`"
         1093             # removing support of floating points because they can't be type checked well
         1094             if [[ "$itertime" != <-> ]]; then
         1095                 unset tombpass
         1096                 unset tombpasstmp
         1097                 _error "Wrong argument for --kdf: must be an integer number (iteration seconds)."
         1098                 _error "Depending on the speed of machines using this tomb, use 1 to 10, or more"
         1099                 return 1
         1100             fi
         1101             # --kdf takes one parameter: iter time (on present machine) in seconds
         1102             local -i microseconds
         1103             microseconds=$(( itertime * 1000000 ))
         1104             _success "Using KDF, iteration time: ::1 microseconds::" $microseconds
         1105             _message "generating salt"
         1106             pbkdf2_salt=`tomb-kdb-pbkdf2-gensalt`
         1107             _message "calculating iterations"
         1108             pbkdf2_iter=`tomb-kdb-pbkdf2-getiter $microseconds`
         1109             _message "encoding the password"
         1110             # We use a length of 64bytes = 512bits (more than needed!?)
         1111             tombpass=`tomb-kdb-pbkdf2 $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"`
         1112 
         1113             header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n"
         1114         }
         1115     }
         1116 
         1117 
         1118     print $header
         1119 
         1120     # TODO: check result of gpg operation
         1121     cat <<EOF | gpg --openpgp --force-mdc --cipher-algo ${algo} \
         1122         --batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
         1123         -o - -c -a
         1124 ${tombpass}
         1125 $TOMBSECRET
         1126 EOF
         1127     # print -n "${tombpass}" \
         1128     #     | gpg --openpgp --force-mdc --cipher-algo ${algo} \
         1129     #     --batch --no-options --no-tty --passphrase-fd 0 --status-fd 2 \
         1130     #     -o - -c -a ${lukskey}
         1131 
         1132     TOMBPASSWORD="$tombpass"    # Set global variable
         1133     unset tombpass
         1134     unset tombpasstmp
         1135 }
         1136 
         1137 # prints an array of ciphers available in gnupg (to encrypt keys)
         1138 list_gnupg_ciphers() {
         1139     # prints an error if GnuPG is not found
         1140     which gpg 2>/dev/null || _failure "gpg (GnuPG) is not found, Tomb cannot function without it."
         1141 
         1142     ciphers=(`gpg --version | awk '
         1143 BEGIN { ciphers=0 }
         1144 /^Cipher:/ { gsub(/,/,""); sub(/^Cipher:/,""); print; ciphers=1; next }
         1145 /^Hash:/ { ciphers=0 }
         1146 { if(ciphers==0) { next } else { gsub(/,/,""); print; } }
         1147 '`)
         1148     print " ${ciphers}"
         1149     return 1
         1150 }
         1151 
         1152 # Steganographic function to bury a key inside an image.
         1153 # Requires steghide(1) to be installed
         1154 bury_key() {
         1155 
         1156     _load_key    # Try loading key from option -k and set TOMBKEY
         1157 
         1158     imagefile=$PARAM
         1159 
         1160     [[ "`file $imagefile`" =~ "JPEG" ]] || {
         1161         _warning "Encode failed: ::1 image file:: is not a jpeg image." $imagefile
         1162         return 1
         1163     }
         1164 
         1165     _success "Encoding key ::1 tomb key:: inside image ::2 image file::" $TOMBKEY $imagefile
         1166     _message "Please confirm the key password for the encoding"
         1167     # We ask the password and test if it is the same encoding the
         1168     # base key, to insure that the same password is used for the
         1169     # encryption and the steganography. This is a standard enforced
         1170     # by Tomb, but it isn't strictly necessary (and having different
         1171     # password would enhance security). Nevertheless here we prefer
         1172     # usability.
         1173 
         1174     { option_is_set --tomb-pwd } && {
         1175         local tombpwd="`option_value --tomb-pwd`"
         1176         _verbose "tomb-pwd = ::1 tomb pass::" $tombpwd
         1177         ask_key_password "$tombpwd"
         1178     } || {
         1179         ask_key_password
         1180     }
         1181     [[ $? != 0 ]] && {
         1182         _warning "Wrong password supplied."
         1183         _failure "You shall not bury a key whose password is unknown to you." }
         1184 
         1185     # We omit armor strings since having them as constants can give
         1186     # ground to effective attacks on steganography
         1187     print - "$TOMBKEY" | awk '
         1188 /^-----/ {next}
         1189 /^Version/ {next}
         1190 {print $0}' \
         1191     | steghide embed --embedfile - --coverfile ${imagefile} \
         1192     -p $TOMBPASSWORD -z 9 -e serpent cbc
         1193     if [ $? != 0 ]; then
         1194         _warning "Encoding error: steghide reports problems."
         1195         res=1
         1196     else
         1197         _success "Tomb key encoded succesfully into image ::1 image file::" $imagefile
         1198         res=0
         1199     fi
         1200 
         1201     return $res
         1202 }
         1203 
         1204 # mandatory 1st arg: the image file where key is supposed to be
         1205 # optional 2nd arg: the password to use (same as key, internal use)
         1206 # optional 3rd arg: the key where to save the result (- for stdout)
         1207 exhume_key() {
         1208     [[ "$1" = "" ]] && {
         1209         _failure "Exhume failed, no image specified" }
         1210 
         1211     local imagefile="$1"  # The image file where to look for the key
         1212     local tombpass="$2"   # (Optional) the password to use (internal use)
         1213     local destkey="$3"    # (Optional) the key file where to save the
         1214     # result (- for stdout)
         1215     local r=1             # Return code (default: fail)
         1216 
         1217     # Ensure the image file is a readable JPEG
         1218     [[ ! -r $imagefile ]] && {
         1219         _failure "Exhume failed, image file not found: ::1 image file::" "${imagefile:-none}" }
         1220     [[ ! $(file "$imagefile") =~ "JP.G" ]] && {
         1221         _failure "Exhume failed: ::1 image file:: is not a jpeg image." $imagefile }
         1222 
         1223     # When a password is passed as argument then always print out
         1224     # the exhumed key on stdout without further checks (internal use)
         1225     [[ -n "$tombpass" ]] && {
         1226         TOMBKEY=$(steghide extract -sf $imagefile -p $tombpass -xf -)
         1227         [[ $? != 0 ]] && {
         1228             _failure "Wrong password or no steganographic key found" }
         1229 
         1230         recover_key $TOMBKEY
         1231 
         1232         return 0
         1233     }
         1234 
         1235     # Ensure we have a valid destination for the key
         1236     [[ -z $destkey ]] && { option_is_set -k } && destkey=$(option_value -k)
         1237     [[ -z $destkey ]] && {
         1238         destkey="-" # No key was specified: fallback to stdout
         1239         _message "printing exhumed key on stdout" }
         1240 
         1241     # Bail out if destination exists, unless -f (force) was passed
         1242     [[ $destkey != "-" && -s $destkey ]] && {
         1243         _warning "File exists: ::1 tomb key::" $destkey
         1244         { option_is_set -f } && {
         1245             _warning "Use of --force selected: overwriting."
         1246             rm -f $destkey
         1247         } || {
         1248             _warning "Make explicit use of --force to overwrite."
         1249             _failure "Refusing to overwrite file. Operation aborted." }
         1250     }
         1251 
         1252     _message "Trying to exhume a key out of image ::1 image file::" $imagefile
         1253     { option_is_set --tomb-pwd } && {
         1254         tombpass=$(option_value --tomb-pwd)
         1255         _verbose "tomb-pwd = ::1 tomb pass::" $tombpass
         1256     } || {
         1257         [[ -n $TOMBPASSWORD ]] && tombpass=$TOMBPASSWORD
         1258     } || {
         1259         tombpass=$(ask_password "Insert password to exhume key from $imagefile")
         1260         [[ $? != 0 ]] && {
         1261             _warning "User aborted password dialog."
         1262             return 1
         1263         }
         1264     }
         1265 
         1266     # Extract the key from the image
         1267     steghide extract -sf $imagefile -p ${tombpass} -xf $destkey
         1268     r=$?
         1269 
         1270     # Report to the user
         1271     [[ "$destkey" = "-" ]] && destkey="stdout"
         1272     [[ $r == 0 ]] && {
         1273         _success "Key succesfully exhumed to ::1 key::." $destkey
         1274     } || {
         1275         _warning "Nothing found in ::1 image file::" $imagefile
         1276     }
         1277 
         1278     return $r
         1279 }
         1280 
         1281 # Produces a printable image of the key contents so a backup on paper
         1282 # can be made and hidden in books etc.
         1283 engrave_key() {
         1284 
         1285     _load_key    # Try loading key from option -k and set TOMBKEYFILE
         1286 
         1287     local keyname=$(basename $TOMBKEYFILE)
         1288     local pngname="$keyname.qr.png"
         1289 
         1290     _success "Rendering a printable QRCode for key: ::1 tomb key file::" $TOMBKEYFILE
         1291     # we omit armor strings to save space
         1292     awk '/^-----/ {next}; /^Version/ {next}; {print $0}' $TOMBKEYFILE \
         1293         | qrencode --size 4 --level H --casesensitive -o $pngname
         1294     [[ $? != 0 ]] && {
         1295         _failure "QREncode reported an error." }
         1296 
         1297     _success "Operation successful:"
         1298     # TODO: only if verbose and/or not silent
         1299     ls -lh $pngname
         1300     file $pngname
         1301 }
         1302 
         1303 # }}} - Key handling
         1304 
         1305 # {{{ Create
         1306 
         1307 # Since version 1.5.3, tomb creation is a three-step process that replaces create_tomb():
         1308 #
         1309 # * dig a .tomb (the large file) using /dev/urandom (takes some minutes at least)
         1310 #
         1311 # * forge a .key (the small file) using /dev/random (good entropy needed)
         1312 #
         1313 # * lock the .tomb file with the key, binding the key to the tomb (requires dm_crypt format)
         1314 
         1315 # Step one - Dig a tomb
         1316 #
         1317 # Synopsis: dig_tomb /path/to/tomb -s sizemebibytes
         1318 #
         1319 # It will create an empty file to be formatted as a loopback
         1320 # filesystem.  Initially the file is filled with random data taken
         1321 # from /dev/urandom to improve overall tomb's security and prevent
         1322 # some attacks aiming at detecting how much data is in the tomb, or
         1323 # which blocks in the filesystem contain that data.
         1324 
         1325 dig_tomb() {
         1326     local    tombpath="$1"    # Path to tomb
         1327     # Require the specification of the size of the tomb (-s) in MiB
         1328     local -i tombsize=$(option_value -s)
         1329 
         1330     _message "Commanded to dig tomb ::1 tomb path::" $tombpath
         1331 
         1332     [[ -n "$tombpath"   ]] || _failure "Missing path to tomb"
         1333     [[ -n "$tombsize"   ]] || _failure "Size argument missing, use -s"
         1334     [[ $tombsize == <-> ]] || _failure "Size must be an integer (mebibytes)"
         1335     [[ $tombsize -ge 10 ]] || _failure "Tombs can't be smaller than 10 mebibytes"
         1336 
         1337     _plot $tombpath          # Set TOMB{PATH,DIR,FILE,NAME}
         1338 
         1339     [[ -e $TOMBPATH ]] && {
         1340         _warning "A tomb exists already. I'm not digging here:"
         1341         ls -lh $TOMBPATH
         1342         return 1
         1343     }
         1344 
         1345     _success "Creating a new tomb in ::1 tomb path::" $TOMBPATH
         1346 
         1347     _message "Generating ::1 tomb file:: of ::2 size::MiB" $TOMBFILE $tombsize
         1348 
         1349     # Ensure that file permissions are safe even if interrupted
         1350     touch $TOMBPATH
         1351     [[ $? = 0 ]] || {
         1352         _warning "Error creating the tomb ::1 tomb path::" $TOMBPATH
         1353         _failure "Operation aborted."
         1354     }
         1355     chmod 0600 $TOMBPATH
         1356 
         1357     _verbose "Data dump using ::1:: from /dev/urandom" ${DD[1]}
         1358     ${=DD} if=/dev/urandom bs=1048576 count=$tombsize of=$TOMBPATH
         1359 
         1360     [[ $? == 0 && -e $TOMBPATH ]] && {
         1361         ls -lh $TOMBPATH
         1362     } || {
         1363         _warning "Error creating the tomb ::1 tomb path::" $TOMBPATH
         1364         _failure "Operation aborted."
         1365     }
         1366 
         1367     _success "Done digging ::1 tomb name::" $TOMBNAME
         1368     _message "Your tomb is not yet ready, you need to forge a key and lock it:"
         1369     _message "tomb forge ::1 tomb path::.key" $TOMBPATH
         1370     _message "tomb lock ::1 tomb path:: -k ::1 tomb path::.key" $TOMBPATH
         1371 
         1372     return 0
         1373 }
         1374 
         1375 # Step two -- Create a detached key to lock a tomb with
         1376 #
         1377 # Synopsis: forge_key [destkey|-k destkey] [-o cipher]
         1378 #
         1379 # Arguments:
         1380 # -k                path to destination keyfile
         1381 # -o                Use an alternate algorithm
         1382 #
         1383 forge_key() {
         1384     # can be specified both as simple argument or using -k
         1385     local destkey="$1"
         1386     { option_is_set -k } && { destkey=$(option_value -k) }
         1387 
         1388     local algo="AES256"  # Default encryption algorithm
         1389 
         1390     [[ -z "$destkey" ]] && {
         1391         _failure "A filename needs to be specified using -k to forge a new key." }
         1392 
         1393 #    _message "Commanded to forge key ::1 key::" $destkey
         1394 
         1395     _check_swap # Ensure the available memory is safe to use
         1396 
         1397     # Ensure GnuPG won't exit with an error before first run
         1398     [[ -r $HOME/.gnupg/pubring.gpg ]] || {
         1399         mkdir -m 0700 $HOME/.gnupg
         1400         touch $HOME/.gnupg/pubring.gpg }
         1401 
         1402     # Do not overwrite any files accidentally
         1403     [[ -r "$destkey" ]] && {
         1404         ls -lh $destkey
         1405         _failure "Forging this key would overwrite an existing file. Operation aborted." }
         1406 
         1407     touch $destkey
         1408     [[ $? == 0 ]] || {
         1409         _warning "Cannot generate encryption key."
         1410         _failure "Operation aborted." }
         1411     chmod 0600 $destkey
         1412 
         1413     # Update algorithm if it was passed on the command line with -o
         1414     { option_is_set -o } && algopt="$(option_value -o)"
         1415     [[ -n "$algopt" ]] && algo=$algopt
         1416 
         1417     _message "Commanded to forge key ::1 key:: with cipher algorithm ::2 algorithm::" \
         1418         $destkey $algo
         1419 
         1420     [[ $KDF == 1 ]] && {
         1421         _message "Using KDF to protect the key password (`option_value --kdf` rounds)"
         1422     }
         1423 
         1424     TOMBKEYFILE="$destkey"    # Set global variable
         1425 
         1426     _warning "This operation takes time, keep using this computer on other tasks,"
         1427     _warning "once done you will be asked to choose a password for your tomb."
         1428     _warning "To make it faster you can move the mouse around."
         1429     _warning "If you are on a server, you can use an Entropy Generation Daemon."
         1430 
         1431     # Use /dev/random as the entropy source, unless --use-urandom is specified
         1432     local random_source=/dev/random
         1433     { option_is_set --use-urandom } && random_source=/dev/urandom
         1434 
         1435     _verbose "Data dump using ::1:: from ::2 source::" ${DD[1]} $random_source
         1436     TOMBSECRET=$(${=DD} bs=1 count=256 if=$random_source)
         1437     [[ $? == 0 ]] || {
         1438         _warning "Cannot generate encryption key."
         1439         _failure "Operation aborted." }
         1440 
         1441     # Here the global variable TOMBSECRET contains the naked secret
         1442 
         1443     _success "Choose the  password of your key: ::1 tomb key::" $TOMBKEYFILE
         1444     _message "(You can also change it later using 'tomb passwd'.)"
         1445     # _user_file $TOMBKEYFILE
         1446 
         1447     tombname="$TOMBKEYFILE" # XXX ???
         1448     # the gen_key() function takes care of the new key's encryption
         1449     { option_is_set --tomb-pwd } && {
         1450         local tombpwd="`option_value --tomb-pwd`"
         1451         _verbose "tomb-pwd = ::1 new pass::" $tombpwd
         1452         gen_key "$tombpwd" >> $TOMBKEYFILE
         1453     } || {
         1454         gen_key >> $TOMBKEYFILE
         1455     }
         1456 
         1457     # load the key contents (set global variable)
         1458     TOMBKEY="`cat $TOMBKEYFILE`"
         1459 
         1460     # this does a check on the file header
         1461     is_valid_key $TOMBKEY || {
         1462         _warning "The key does not seem to be valid."
         1463         _warning "Dumping contents to screen:"
         1464         print "`cat $TOMBKEY`"
         1465         _warning "--"
         1466         _sudo umount ${keytmp}
         1467         rm -r $keytmp
         1468         _failure "Operation aborted."
         1469     }
         1470 
         1471     _message "Done forging ::1 key file::" $TOMBKEYFILE
         1472     _success "Your key is ready:"
         1473     ls -lh $TOMBKEYFILE
         1474 }
         1475 
         1476 # Step three -- Lock tomb
         1477 #
         1478 # Synopsis: tomb_lock file.tomb file.tomb.key [-o cipher]
         1479 #
         1480 # Lock the given tomb with the given key file, in fact formatting the
         1481 # loopback volume as a LUKS device.
         1482 # Default cipher 'aes-xts-plain64:sha256'can be overridden with -o
         1483 lock_tomb_with_key() {
         1484     # old default was aes-cbc-essiv:sha256
         1485     # Override with -o
         1486     # for more alternatives refer to cryptsetup(8)
         1487     local cipher="aes-cbc-essiv:sha256"
         1488 
         1489     local tombpath="$1"      # First argument is the path to the tomb
         1490 
         1491     [[ -n $tombpath ]] || {
         1492         _warning "No tomb specified for locking."
         1493         _warning "Usage: tomb lock file.tomb file.tomb.key"
         1494         return 1
         1495     }
         1496 
         1497     _plot $tombpath
         1498 
         1499     _message "Commanded to lock tomb ::1 tomb file::" $TOMBFILE
         1500 
         1501     [[ -f $TOMBPATH ]] || {
         1502         _failure "There is no tomb here. You have to dig it first." }
         1503 
         1504     _verbose "Tomb found: ::1 tomb path::" $TOMBPATH
         1505 
         1506     lo_mount $TOMBPATH
         1507     nstloop=`lo_new`
         1508 
         1509     _verbose "Loop mounted on ::1 mount point::" $nstloop
         1510 
         1511     _message "Checking if the tomb is empty (we never step on somebody else's bones)."
         1512     su -c cryptsetup isLuks ${nstloop}
         1513     if [[ $? = 0 ]]; then
         1514         # is it a LUKS encrypted nest? then bail out and avoid reformatting it
         1515         _warning "The tomb was already locked with another key."
         1516         _failure "Operation aborted. I cannot lock an already locked tomb. Go dig a new one."
         1517     else
         1518         _message "Fine, this tomb seems empty."
         1519     fi
         1520 
         1521     _load_key    # Try loading key from option -k and set TOMBKEYFILE
         1522 
         1523     # the encryption cipher for a tomb can be set when locking using -c
         1524     { option_is_set -o } && algopt="$(option_value -o)"
         1525     [[ -n "$algopt" ]] && cipher=$algopt
         1526     _message "Locking using cipher: ::1 cipher::" $cipher
         1527 
         1528     # get the pass from the user and check it
         1529     if option_is_set --tomb-pwd; then
         1530         tomb_pwd="`option_value --tomb-pwd`"
         1531         _verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd
         1532         ask_key_password "$tomb_pwd"
         1533     else
         1534         ask_key_password
         1535     fi
         1536     [[ $? == 0 ]] || _failure "No valid password supplied."
         1537 
         1538     _success "Locking ::1 tomb file:: with ::2 tomb key file::" $TOMBFILE $TOMBKEYFILE
         1539 
         1540     _message "Formatting Luks mapped device."
         1541     _cryptsetup --batch-mode \
         1542         --cipher ${cipher} --key-size 256 --key-slot 0 \
         1543         luksFormat ${nstloop}
         1544     [[ $? == 0 ]] || {
         1545         _warning "cryptsetup luksFormat returned an error."
         1546         _failure "Operation aborted." }
         1547 
         1548     _cryptsetup --cipher ${cipher} luksOpen ${nstloop} tomb.tmp
         1549     [[ $? == 0 ]] || {
         1550         _warning "cryptsetup luksOpen returned an error."
         1551         _failure "Operation aborted." }
         1552 
         1553     _message "Formatting your Tomb with Ext3/Ext2 filesystem."
         1554     _sudo mkfs.ext2 -q -F -j -L $TOMBNAME /dev/mapper/tomb.tmp
         1555 
         1556     [[ $? == 0 ]] || {
         1557         _warning "Tomb format returned an error."
         1558         _warning "Your tomb ::1 tomb file:: may be corrupted." $TOMBFILE }
         1559 
         1560     # Sync
         1561     _sudo cryptsetup luksClose tomb.tmp
         1562 
         1563     _message "Done locking ::1 tomb name:: using Luks dm-crypt ::2 cipher::" $TOMBNAME $cipher
         1564     _success "Your tomb is ready in ::1 tomb path:: and secured with key ::2 tomb key::" \
         1565         $TOMBPATH $TOMBKEYFILE
         1566 
         1567 }
         1568 
         1569 # This function changes the key that locks a tomb
         1570 change_tomb_key() {
         1571     local tombkey="$1"      # Path to the tomb's key file
         1572     local tombpath="$2"     # Path to the tomb
         1573 
         1574     _message "Commanded to reset key for tomb ::1 tomb path::" $tombpath
         1575 
         1576     [[ -z "$tombpath" ]] && {
         1577         _warning "Command 'setkey' needs two arguments: the old key file and the tomb."
         1578         _warning "I.e:  tomb -k new.tomb.key old.tomb.key secret.tomb"
         1579         _failure "Execution aborted."
         1580     }
         1581 
         1582     _check_swap
         1583 
         1584     # this also calls _plot()
         1585     is_valid_tomb $tombpath
         1586 
         1587     lo_mount $TOMBPATH
         1588     nstloop=`lo_new`
         1589     _sudo cryptsetup isLuks ${nstloop}
         1590     # is it a LUKS encrypted nest? we check one more time
         1591     [[ $? == 0 ]] || {
         1592         _failure "Not a valid LUKS encrypted volume: ::1 volume::" $TOMBPATH }
         1593 
         1594     _load_key $tombkey    # Try loading given key and set TOMBKEY and
         1595     # TOMBKEYFILE
         1596     local oldkey=$TOMBKEY
         1597     local oldkeyfile=$TOMBKEYFILE
         1598 
         1599     # we have everything, prepare to mount
         1600     _success "Changing lock on tomb ::1 tomb name::" $TOMBNAME
         1601     _message "Old key: ::1 old key::" $oldkeyfile
         1602 
         1603     # render the mapper
         1604     mapdate=`date +%s`
         1605     # save date of mount in minutes since 1970
         1606     mapper="tomb.$TOMBNAME.$mapdate.$(basename $nstloop)"
         1607 
         1608     # load the old key
         1609     if option_is_set --tomb-old-pwd; then
         1610         tomb_old_pwd="`option_value --tomb-old-pwd`"
         1611         _verbose "tomb-old-pwd = ::1 old pass::" $tomb_old_pwd
         1612         ask_key_password "$tomb_old_pwd"
         1613     else
         1614         ask_key_password
         1615     fi
         1616     [[ $? == 0 ]] || {
         1617         _failure "No valid password supplied for the old key." }
         1618     old_secret=$TOMBSECRET
         1619 
         1620     # luksOpen the tomb (not really mounting, just on the loopback)
         1621     print -R -n - "$old_secret" | _sudo cryptsetup --key-file - \
         1622         luksOpen ${nstloop} ${mapper}
         1623     [[ $? == 0 ]] || _failure "Unexpected error in luksOpen."
         1624 
         1625     _load_key # Try loading new key from option -k and set TOMBKEYFILE
         1626 
         1627     _message "New key: ::1 key file::" $TOMBKEYFILE
         1628 
         1629     if option_is_set --tomb-pwd; then
         1630         tomb_new_pwd="`option_value --tomb-pwd`"
         1631         _verbose "tomb-pwd = ::1 tomb pass::" $tomb_new_pwd
         1632         ask_key_password "$tomb_new_pwd"
         1633     else
         1634         ask_key_password
         1635     fi
         1636     [[ $? == 0 ]] || {
         1637         _failure "No valid password supplied for the new key." }
         1638 
         1639     _tmp_create
         1640     tmpnewkey=$TOMBTMP
         1641     print -R -n - "$TOMBSECRET" >> $tmpnewkey
         1642 
         1643     print -R -n - "$old_secret" | _sudo cryptsetup --key-file - \
         1644         luksChangeKey "$nstloop" "$tmpnewkey"
         1645 
         1646     [[ $? == 0 ]] || _failure "Unexpected error in luksChangeKey."
         1647 
         1648     _sudo cryptsetup luksClose "${mapper}" || _failure "Unexpected error in luksClose."
         1649 
         1650     _success "Succesfully changed key for tomb: ::1 tomb file::" $TOMBFILE
         1651     _message "The new key is: ::1 new key::" $TOMBKEYFILE
         1652 
         1653     return 0
         1654 }
         1655 
         1656 # }}} - Creation
         1657 
         1658 # {{{ Open
         1659 
         1660 # $1 = tombfile $2(optional) = mountpoint
         1661 mount_tomb() {
         1662     local tombpath="$1"    # First argument is the path to the tomb
         1663     [[ -n "$tombpath" ]] || _failure "No tomb name specified for opening."
         1664 
         1665     _message "Commanded to open tomb ::1 tomb name::" $tombpath
         1666 
         1667     _check_swap
         1668 
         1669     # this also calls _plot()
         1670     is_valid_tomb $tombpath
         1671 
         1672     _load_key # Try loading new key from option -k and set TOMBKEYFILE
         1673 
         1674     tombmount="$2"
         1675     [[ "$tombmount" = "" ]] && {
         1676         # Android default in app's home/media
         1677         tombmount="$HOME/media/$TOMBNAME"
         1678         mkdir -p $tombmount
         1679         _message "Mountpoint not specified, using default: ::1 mount point::" $tombmount
         1680     }
         1681 
         1682     _success "Opening ::1 tomb file:: on ::2 mount point::" $TOMBNAME $tombmount
         1683 
         1684     lo_mount $TOMBPATH
         1685     nstloop=`lo_new`
         1686 
         1687     _sudo cryptsetup isLuks ${nstloop} || {
         1688         # is it a LUKS encrypted nest? see cryptsetup(1)
         1689         _failure "::1 tomb file:: is not a valid Luks encrypted storage file." $TOMBFILE }
         1690 
         1691     _message "This tomb is a valid LUKS encrypted device."
         1692 
         1693     luksdump="`_sudo cryptsetup luksDump ${nstloop}`"
         1694     tombdump=(`print $luksdump | awk '
         1695         /^Cipher name/ {print $3}
         1696         /^Cipher mode/ {print $3}
         1697         /^Hash spec/   {print $3}'`)
         1698     _message "Cipher is \"::1 cipher::\" mode \"::2 mode::\" hash \"::3 hash::\"" $tombdump[1] $tombdump[2] $tombdump[3]
         1699 
         1700     slotwarn=`print $luksdump | awk '
         1701         BEGIN { zero=0 }
         1702         /^Key slot 0/ { zero=1 }
         1703         /^Key slot.*ENABLED/ { if(zero==1) print "WARN" }'`
         1704     [[ "$slotwarn" == "WARN" ]] && {
         1705         _warning "Multiple key slots are enabled on this tomb. Beware: there can be a backdoor." }
         1706 
         1707     # save date of mount in minutes since 1970
         1708     mapdate=`date +%s`
         1709 
         1710     mapper="tomb.$TOMBNAME.$mapdate.$(basename $nstloop)"
         1711 
         1712     _verbose "dev mapper device: ::1 mapper::" $mapper
         1713     _verbose "Tomb key: ::1 key file::" $TOMBKEYFILE
         1714 
         1715     # take the name only, strip extensions
         1716     _verbose "Tomb name: ::1 tomb name:: (to be engraved)" $TOMBNAME
         1717 
         1718     { option_is_set --tomb-pwd } && {
         1719         tomb_pwd="`option_value --tomb-pwd`"
         1720         _verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd
         1721         ask_key_password "$tomb_pwd"
         1722     } || {
         1723         ask_key_password
         1724     }
         1725     [[ $? == 0 ]] || _failure "No valid password supplied."
         1726 
         1727     _cryptsetup luksOpen ${nstloop} ${mapper}
         1728     [[ $? = 0 ]] || {
         1729         _failure "Failure mounting the encrypted file." }
         1730 
         1731     # preserve the loopdev after exit
         1732     lo_preserve "$nstloop"
         1733 
         1734     # array: [ cipher, keysize, loopdevice ]
         1735     tombstat=(`_sudo cryptsetup status ${mapper} | awk '
         1736     /cipher:/  {print $2}
         1737     /keysize:/ {print $2}
         1738     /device:/  {print $2}'`)
         1739     _success "Success unlocking tomb ::1 tomb name::" $TOMBNAME
         1740     _verbose "Key size is ::1 size:: for cipher ::2 cipher::" $tombstat[2] $tombstat[1]
         1741 
         1742     _message "Checking filesystem via ::1::" $tombstat[3]
         1743     _sudo e2fsck -p -C0 /dev/mapper/${mapper}
         1744     _verbose "Tomb engraved as ::1 tomb name::" $TOMBNAME
         1745     _sudo tune2fs -L $TOMBNAME /dev/mapper/${mapper} > /dev/null
         1746 
         1747     # we need root from here on
         1748     _sudo mkdir -p $tombmount
         1749 
         1750     # Default mount options are overridden with the -o switch
         1751     { option_is_set -o } && {
         1752         local oldmountopts=$MOUNTOPTS
         1753         MOUNTOPTS="$(option_value -o)" }
         1754 
         1755     # TODO: safety check MOUNTOPTS
         1756     # safe_mount_options && \
         1757     _sudo mount -t ext2 -o $MOUNTOPTS /dev/mapper/${mapper} ${tombmount}
         1758     # Clean up if the mount failed
         1759     [[ $? == 0 ]] || {
         1760         _warning "Error mounting ::1 mapper:: on ::2 tombmount::" $mapper $tombmount
         1761         [[ $oldmountopts != $MOUNTOPTS ]] && \
         1762           _warning "Are mount options '::1 mount options::' valid?" $MOUNTOPTS
         1763         # TODO: move cleanup to _endgame()
         1764         [[ -d $tombmount ]] && _sudo rmdir $tombmount
         1765         [[ -e /dev/mapper/$mapper ]] && _sudo cryptsetup luksClose $mapper
         1766         # The loop is taken care of in _endgame()
         1767         _failure "Cannot mount ::1 tomb name::" $TOMBNAME
         1768     }
         1769 
         1770     # Add to Android's own mtab
         1771     mount_add_tomb_mtab /dev/mapper/$mapper $tombmount
         1772 
         1773     _sudo chown $UID:$GID ${tombmount}
         1774     _sudo chmod 0711 ${tombmount}
         1775 
         1776     _success "Success opening ::1 tomb file:: on ::2 mount point::" $TOMBFILE $tombmount
         1777 
         1778     local tombtty tombhost tombuid tombuser
         1779 
         1780     # print out when it was opened the last time, by whom and where
         1781 #    [[ -r ${tombmount}/.last ]] && {
         1782 #        tombsince=$(_cat ${tombmount}/.last)
         1783 #        tombsince=$(date --date=@$tombsince +%c)
         1784 #        tombtty=$(_cat ${tombmount}/.tty)
         1785 #        tombhost=$(_cat ${tombmount}/.host)
         1786 #        tomblast=$(_cat ${tombmount}/.last)
         1787 #        tombuid=$(_cat ${tombmount}/.uid | tr -d ' ')
         1788 
         1789 #        tombuser=$(getent passwd $tombuid)
         1790 #        tombuser=${tombuser[(ws@:@)1]}
         1791 
         1792 #        _message "Last visit by ::1 user::(::2 tomb build::) from ::3 tty:: on ::4 host::" $tombuser $tombuid $tombtty $tombhost
         1793 #        _message "on date ::1 date::" $tombsince
         1794 #    }
         1795     # write down the UID and TTY that opened the tomb
         1796 #    rm -f ${tombmount}/.uid
         1797 #    print $_UID > ${tombmount}/.uid
         1798 #    rm -f ${tombmount}/.tty
         1799 #    print $_TTY > ${tombmount}/.tty
         1800     # also the hostname
         1801     # rm -f ${tombmount}/.host
         1802     # hostname > ${tombmount}/.host
         1803     # and the "last time opened" information
         1804     # in minutes since 1970, this is printed at next open
         1805     rm -f ${tombmount}/.last
         1806     date +%s > ${tombmount}/.last
         1807     # human readable: date --date=@"`cat .last`" +%c
         1808 
         1809 
         1810     # process bind-hooks (mount -o bind of directories)
         1811     # and post-hooks (execute on open)
         1812     { option_is_set -n } || {
         1813         exec_safe_bind_hooks ${tombmount}
         1814         exec_safe_post_hooks ${tombmount} open }
         1815 
         1816     return 0
         1817 }
         1818 
         1819 ## HOOKS EXECUTION
         1820 #
         1821 # Execution of code inside a tomb may present a security risk, e.g.,
         1822 # if the tomb is shared or compromised, an attacker could embed
         1823 # malicious code.  When in doubt, open the tomb with the -n switch in
         1824 # order to skip this feature and verify the files mount-hooks and
         1825 # bind-hooks inside the tomb yourself before letting them run.
         1826 
         1827 # Mount files and directories from the tomb to the current user's HOME.
         1828 #
         1829 # Synopsis: exec_safe_bind_hooks /path/to/mounted/tomb
         1830 #
         1831 # This can be a security risk if you share tombs with untrusted people.
         1832 # In that case, use the -n switch to turn off this feature.
         1833 exec_safe_bind_hooks() {
         1834     local mnt="$1"   # First argument is the mount point of the tomb
         1835 
         1836     # Default mount options are overridden with the -o switch
         1837     [[ -n ${(k)OPTS[-o]} ]] && MOUNTOPTS=${OPTS[-o]}
         1838 
         1839     # No HOME set? Note: this should never happen again.
         1840     [[ -z $HOME ]] && {
         1841         _warning "How pitiful!  A tomb, and no HOME."
         1842         return 1 }
         1843 
         1844     [[ -z $mnt || ! -d $mnt ]] && {
         1845         _warning "Cannot exec bind hooks without a mounted tomb."
         1846         return 1 }
         1847 
         1848     [[ -r "$mnt/bind-hooks" ]] || {
         1849         _verbose "bind-hooks not found in ::1 mount point::" $mnt
         1850         return 1 }
         1851 
         1852     typeset -Al maps        # Maps of files and directories to mount
         1853     typeset -al mounted     # Track already mounted files and directories
         1854 
         1855     # better parsing for bind hooks checks for two separated words on
         1856     # each line, using zsh word separator array subscript
         1857     _bindhooks="${mapfile[${mnt}/bind-hooks]}"
         1858     for h in ${(f)_bindhooks}; do
         1859         s="${h[(w)1]}"
         1860         d="${h[(w)2]}"
         1861         [[ "$s" = "" ]] && { _warning "bind-hooks file is broken"; return 1 }
         1862         [[ "$d" = "" ]] && { _warning "bind-hooks file is broken"; return 1 }
         1863         maps+=($s $d)
         1864         _verbose "bind-hook found: $s -> $d"
         1865     done
         1866     unset _bindhooks
         1867 
         1868     for dir in ${(k)maps}; do
         1869         [[ "${dir[1]}" == "/" || "${dir[1,2]}" == ".." ]] && {
         1870             _warning "bind-hooks map format: local/to/tomb local/to/\$HOME"
         1871             continue }
         1872 
         1873         [[ "${${maps[$dir]}[1]}" == "/" || "${${maps[$dir]}[1,2]}" == ".." ]] && {
         1874             _warning "bind-hooks map format: local/to/tomb local/to/\$HOME.  Rolling back"
         1875             for dir in ${mounted}; do _sudo umount $dir; done
         1876             return 1 }
         1877 
         1878         if [[ ! -r "$HOME/${maps[$dir]}" ]]; then
         1879             _warning "bind-hook target not existent, skipping ::1 home::/::2 subdir::" $HOME ${maps[$dir]}
         1880         elif [[ ! -r "$mnt/$dir" ]]; then
         1881             _warning "bind-hook source not found in tomb, skipping ::1 mount point::/::2 subdir::" $mnt $dir
         1882         else
         1883             _sudo mount -o bind,$MOUNTOPTS $mnt/$dir $HOME/${maps[$dir]} \
         1884                 && mounted+=("$HOME/${maps[$dir]}")
         1885         fi
         1886     done
         1887 }
         1888 
         1889 # Execute automated actions configured in the tomb.
         1890 #
         1891 # Synopsis: exec_safe_post_hooks /path/to/mounted/tomb [open|close]
         1892 #
         1893 # If an executable file named 'post-hooks' is found inside the tomb,
         1894 # run it as a user.  This might need a dialog for security on what is
         1895 # being run, however we expect you know well what is inside your tomb.
         1896 # If you're mounting an untrusted tomb, be safe and use the -n switch
         1897 # to verify what it would run if you let it.  This feature opens the
         1898 # possibility to make encrypted executables.
         1899 exec_safe_post_hooks() {
         1900     local mnt=$1     # First argument is where the tomb is mounted
         1901     local act=$2     # Either 'open' or 'close'
         1902 
         1903     # Only run if post-hooks has the executable bit set
         1904     [[ -x $mnt/post-hooks ]] || return
         1905 
         1906     # If the file starts with a shebang, run it.
         1907     cat $mnt/post-hooks | head -n1 | grep '^#!\s*/' &> /dev/null
         1908     [[ $? == 0 ]] && {
         1909         _success "Post hooks found, executing as user ::1 user name::." $USERNAME
         1910         $mnt/post-hooks $act $mnt
         1911     }
         1912 }
         1913 
         1914 # }}} - Tomb open
         1915 
         1916 # {{{ List
         1917 
         1918 # list all tombs mounted in a readable format
         1919 # $1 is optional, to specify a tomb
         1920 list_tombs() {
         1921 
         1922     local tombname tombmount tombfs tombfsopts tombloop
         1923     local ts tombtot tombused tombavail tombpercent tombp tombsince
         1924     local tombtty tombhost tombuid tombuser
         1925     # list all open tombs
         1926     mounted_tombs=(`list_tomb_mounts $1`)
         1927     [[ ${#mounted_tombs} == 0 ]] && {
         1928         _failure "I can't see any ::1 status:: tomb, may they all rest in peace." ${1:-open} }
         1929 
         1930     for t in ${mounted_tombs}; do
         1931         mapper=`basename ${t[(ws:;:)1]}`
         1932         tombname=${t[(ws:;:)5]}
         1933         tombmount=${t[(ws:;:)2]}
         1934         tombfs=${t[(ws:;:)3]}
         1935         tombfsopts=${t[(ws:;:)4]}
         1936         tombloop=${mapper[(ws:.:)4]}
         1937 
         1938         # calculate tomb size (TODO Android)
         1939 #        ts=`df /dev/mapper/$mapper |
         1940 #awk "/mapper/"' { print $2 ";" $3 ";" $4 ";" $5 }'`
         1941 #        tombtot=${ts[(ws:;:)1]}
         1942 #        tombused=${ts[(ws:;:)2]}
         1943 #        tombavail=${ts[(ws:;:)3]}
         1944 #        tombpercent=${ts[(ws:;:)4]}
         1945 #        tombp=${tombpercent%%%}
         1946 
         1947         # obsolete way to get the last open date from /dev/mapper
         1948         # which doesn't work when tomb filename contain dots
         1949         # tombsince=`date --date=@${mapper[(ws:.:)3]} +%c`
         1950 
         1951         # find out who opens it from where
         1952         [[ -r ${tombmount}/.tty ]] && {
         1953 #            tombsince=$(_cat ${tombmount}/.last)
         1954 #            tombsince=$(date --date=@$tombsince +%c)
         1955 #            tombtty=$(_cat ${tombmount}/.tty)
         1956 #            tombhost=$(_cat ${tombmount}/.host)
         1957 #            tombuid=$(_cat ${tombmount}/.uid | tr -d ' ')
         1958 
         1959 #            tombuser=$(getent passwd $tombuid)
         1960             tombuser=${tombuser[(ws@:@)1]}
         1961         }
         1962 
         1963         { option_is_set --get-mountpoint } && { print $tombmount; continue }
         1964 
         1965         _message "::1 tombname:: open on ::2 tombmount:: using ::3 tombfsopts::" \
         1966             $tombname $tombmount $tombfsopts
         1967 
         1968         _verbose "::1 tombname:: /dev/::2 tombloop:: device mounted (detach with losetup -d)" $tombname $tombloop
         1969 
         1970 #        _message "::1 tombname:: open since ::2 tombsince::" $tombname $tombsince
         1971 
         1972 #        [[ -z "$tombtty" ]] || {
         1973 #            _message "::1 tombname:: open by ::2 tombuser:: from ::3 tombtty:: on ::4 tombhost::" \
         1974 #                $tombname $tombuser $tombtty $tombhost
         1975 #        }
         1976 
         1977 #        _message "::1 tombname:: size ::2 tombtot:: of which ::3 tombused:: (::5 tombpercent::%) is used: ::4 tombavail:: free " \
         1978 #            $tombname $tombtot $tombused $tombavail $tombpercent
         1979 
         1980 #        [[ ${tombp} -ge 90 ]] && {
         1981 #            _warning "::1 tombname:: warning: your tomb is almost full!" $tombname
         1982 #        }
         1983 
         1984         # Now check hooks (TODO Android)
         1985 #        mounted_hooks=(`list_tomb_binds $tombname $tombmount`)
         1986 #        for h in ${mounted_hooks}; do
         1987 #            _message "::1 tombname:: hooks ::2 hookname:: on ::3 hookdest::" \
         1988 #                $tombname "`basename ${h[(ws:;:)1]}`" ${h[(ws:;:)2]}
         1989 #        done
         1990     done
         1991 }
         1992 
         1993 
         1994 # Print out an array of mounted tombs (internal use)
         1995 # Format is semi-colon separated list of attributes
         1996 # if 1st arg is supplied, then list only that tomb
         1997 #
         1998 # String positions in the semicolon separated array:
         1999 #
         2000 # 1. full mapper path
         2001 #
         2002 # 2. mountpoint
         2003 #
         2004 # 3. filesystem type
         2005 #
         2006 # 4. mount options
         2007 #
         2008 # 5. tomb name
         2009 list_tomb_mounts() {
         2010     # list all open tombs
         2011     if [[ "$1" = "" ]]; then mount_list;
         2012     # list a specific tomb
         2013     else mount_list | grep "${1}$"; fi
         2014 }
         2015 
         2016 # list_tomb_binds
         2017 # print out an array of mounted bind hooks (internal use)
         2018 # format is semi-colon separated list of attributes
         2019 # needs two arguments: name of tomb whose hooks belong
         2020 #                      mount tomb
         2021 list_tomb_binds() {
         2022     [[ -z "$2" ]] && {
         2023         _failure "Internal error: list_tomb_binds called without argument." }
         2024 
         2025     # OK well, prepare for some insanity: parsing the mount table on GNU/Linux
         2026     # is like combing a Wookie while he is riding a speedbike down a valley.
         2027 
         2028     typeset -A tombs
         2029     typeset -a binds
         2030     for t in "${(f)$(mount_list | grep '/dev/mapper/tomb.*]$')}"; do
         2031         len="${(w)#t}"
         2032         [[ "${t[(w)$len]}" = "$1" ]] || continue
         2033         tombs+=( ${t[(w)1]} ${t[(w)$len]} )
         2034 
         2035     done
         2036 
         2037     for m in ${(k)tombs}; do
         2038         for p in "${(f)$(cat /proc/mounts):s/\\040(deleted)/}"; do
         2039             # Debian's kernel appends a '\040(deleted)' to the mountpoint in /proc/mounts
         2040             # so if we parse the string as-is then this will break the parsing. How nice of them!
         2041             # Some bugs related to this are more than 10yrs old. Such Debian! Much stable! Very parsing!
         2042             # Bug #711183  umount parser for /proc/mounts broken on stale nfs mount (gets renamed to "/mnt/point (deleted)")
         2043             # Bug #711184  mount should not stat mountpoints on mount
         2044             # Bug #711187  linux-image-3.2.0-4-amd64: kernel should not rename mountpoint if nfs server is dead/unreachable
         2045             [[ "${p[(w)1]}" = "$m" ]] && {
         2046                 [[ "${(q)p[(w)2]}" != "${(q)2}" ]] && {
         2047                     # Our output format:
         2048                     # mapper;mountpoint;fs;flags;name
         2049                     binds+=("$m;${(q)p[(w)2]};${p[(w)3]};${p[(w)4]};${tombs[$m]}") }
         2050             }
         2051         done
         2052     done
         2053 
         2054     # print the results out line by line
         2055     for b in $binds; do print - "$b"; done
         2056 }
         2057 
         2058 # }}} - Tomb list
         2059 
         2060 # {{{ Index and search
         2061 
         2062 # index files in all tombs for search
         2063 # $1 is optional, to specify a tomb
         2064 index_tombs() {
         2065     { command -v updatedb 1>/dev/null 2>/dev/null } || {
         2066         _failure "Cannot index tombs on this system: updatedb (mlocate) not installed." }
         2067 
         2068     updatedbver=`updatedb --version | grep '^updatedb'`
         2069     [[ "$updatedbver" =~ "GNU findutils" ]] && {
         2070         _warning "Cannot use GNU findutils for index/search commands." }
         2071     [[ "$updatedbver" =~ "mlocate" ]] || {
         2072         _failure "Index command needs 'mlocate' to be installed." }
         2073 
         2074     _verbose "$updatedbver"
         2075 
         2076     mounted_tombs=(`list_tomb_mounts $1`)
         2077     [[ ${#mounted_tombs} == 0 ]] && {
         2078         # Considering one tomb
         2079         [[ -n "$1" ]] && {
         2080             _failure "There seems to be no open tomb engraved as [::1::]" $1 }
         2081         # Or more
         2082         _failure "I can't see any open tomb, may they all rest in peace." }
         2083 
         2084     _success "Creating and updating search indexes."
         2085 
         2086     # start the LibreOffice document converter if installed
         2087     { command -v unoconv 1>/dev/null 2>/dev/null } && {
         2088         unoconv -l 2>/dev/null &
         2089         _verbose "unoconv listener launched."
         2090         sleep 1 }
         2091 
         2092     for t in ${mounted_tombs}; do
         2093         mapper=`basename ${t[(ws:;:)1]}`
         2094         tombname=${t[(ws:;:)5]}
         2095         tombmount=${t[(ws:;:)2]}
         2096         [[ -r ${tombmount}/.noindex ]] && {
         2097             _message "Skipping ::1 tomb name:: (.noindex found)." $tombname
         2098             continue }
         2099         _message "Indexing ::1 tomb name:: filenames..." $tombname
         2100         updatedb -l 0 -o ${tombmount}/.updatedb -U ${tombmount}
         2101 
         2102         # here we use swish to index file contents
         2103         [[ $SWISH == 1 ]] && {
         2104             _message "Indexing ::1 tomb name:: contents..." $tombname
         2105             rm -f ${tombmount}/.swishrc
         2106             _message "Generating a new swish-e configuration file: ::1 swish conf::" ${tombmount}/.swishrc
         2107             cat <<EOF > ${tombmount}/.swishrc
         2108 # index directives
         2109 DefaultContents TXT*
         2110 IndexDir $tombmount
         2111 IndexFile $tombmount/.swish
         2112 # exclude images
         2113 FileRules filename regex /\.jp.?g/i
         2114 FileRules filename regex /\.png/i
         2115 FileRules filename regex /\.gif/i
         2116 FileRules filename regex /\.tiff/i
         2117 FileRules filename regex /\.svg/i
         2118 FileRules filename regex /\.xcf/i
         2119 FileRules filename regex /\.eps/i
         2120 FileRules filename regex /\.ttf/i
         2121 # exclude audio
         2122 FileRules filename regex /\.mp3/i
         2123 FileRules filename regex /\.ogg/i
         2124 FileRules filename regex /\.wav/i
         2125 FileRules filename regex /\.mod/i
         2126 FileRules filename regex /\.xm/i
         2127 # exclude video
         2128 FileRules filename regex /\.mp4/i
         2129 FileRules filename regex /\.avi/i
         2130 FileRules filename regex /\.ogv/i
         2131 FileRules filename regex /\.ogm/i
         2132 FileRules filename regex /\.mkv/i
         2133 FileRules filename regex /\.mov/i
         2134 FileRules filename regex /\.flv/i
         2135 FileRules filename regex /\.webm/i
         2136 # exclude system
         2137 FileRules filename is ok
         2138 FileRules filename is lock
         2139 FileRules filename is control
         2140 FileRules filename is status
         2141 FileRules filename is proc
         2142 FileRules filename is sys
         2143 FileRules filename is supervise
         2144 FileRules filename regex /\.asc$/i
         2145 FileRules filename regex /\.gpg$/i
         2146 # pdf and postscript
         2147 FileFilter .pdf pdftotext "'%p' -"
         2148 FileFilter .ps  ps2txt "'%p' -"
         2149 # compressed files
         2150 FileFilterMatch lesspipe "%p" /\.tgz$/i
         2151 FileFilterMatch lesspipe "%p" /\.zip$/i
         2152 FileFilterMatch lesspipe "%p" /\.gz$/i
         2153 FileFilterMatch lesspipe "%p" /\.bz2$/i
         2154 FileFilterMatch lesspipe "%p" /\.Z$/
         2155 # spreadsheets
         2156 FileFilterMatch unoconv "-d spreadsheet -f csv --stdout %P" /\.xls.*/i
         2157 FileFilterMatch unoconv "-d spreadsheet -f csv --stdout %P" /\.xlt.*/i
         2158 FileFilter .ods unoconv "-d spreadsheet -f csv --stdout %P"
         2159 FileFilter .ots unoconv "-d spreadsheet -f csv --stdout %P"
         2160 FileFilter .dbf unoconv "-d spreadsheet -f csv --stdout %P"
         2161 FileFilter .dif unoconv "-d spreadsheet -f csv --stdout %P"
         2162 FileFilter .uos unoconv "-d spreadsheet -f csv --stdout %P"
         2163 FileFilter .sxc unoconv "-d spreadsheet -f csv --stdout %P"
         2164 # word documents
         2165 FileFilterMatch unoconv "-d document -f txt --stdout %P" /\.doc.*/i
         2166 FileFilterMatch unoconv "-d document -f txt --stdout %P" /\.odt.*/i
         2167 FileFilterMatch unoconv "-d document -f txt --stdout %P" /\.rtf.*/i
         2168 FileFilterMatch unoconv "-d document -f txt --stdout %P" /\.tex$/i
         2169 # native html support
         2170 IndexContents HTML* .htm .html .shtml
         2171 IndexContents XML*  .xml
         2172 EOF
         2173 
         2174             swish-e -c ${tombmount}/.swishrc -S fs -v3
         2175         }
         2176         _message "Search index updated."
         2177     done
         2178 }
         2179 
         2180 search_tombs() {
         2181     { command -v locate 1>/dev/null 2>/dev/null } || {
         2182         _failure "Cannot index tombs on this system: updatedb (mlocate) not installed." }
         2183 
         2184     updatedbver=`updatedb --version | grep '^updatedb'`
         2185     [[ "$updatedbver" =~ "GNU findutils" ]] && {
         2186         _warning "Cannot use GNU findutils for index/search commands." }
         2187     [[ "$updatedbver" =~ "mlocate" ]] || {
         2188         _failure "Index command needs 'mlocate' to be installed." }
         2189 
         2190     _verbose "$updatedbver"
         2191 
         2192     # list all open tombs
         2193     mounted_tombs=(`list_tomb_mounts`)
         2194     [[ ${#mounted_tombs} == 0 ]] && {
         2195         _failure "I can't see any open tomb, may they all rest in peace." }
         2196 
         2197     _success "Searching for: ::1::" ${(f)@}
         2198     for t in ${mounted_tombs}; do
         2199         _verbose "Checking for index: ::1::" ${t}
         2200         mapper=`basename ${t[(ws:;:)1]}`
         2201         tombname=${t[(ws:;:)5]}
         2202         tombmount=${t[(ws:;:)2]}
         2203         [[ -r ${tombmount}/.updatedb ]] && {
         2204             # Use mlocate to search hits on filenames
         2205             _message "Searching filenames in tomb ::1 tomb name::" $tombname
         2206             locate -d ${tombmount}/.updatedb -e -i "${(f)@}"
         2207             _message "Matches found: ::1 matches::" \
         2208                 $(locate -d ${tombmount}/.updatedb -e -i -c ${(f)@})
         2209 
         2210             # Use swish-e to search over contents
         2211             [[ $SWISH == 1 && -r $tombmount/.swish ]] && {
         2212                 _message "Searching contents in tomb ::1 tomb name::" $tombname
         2213                 swish-e -w ${=@} -f $tombmount/.swish -H0 }
         2214         } || {
         2215             _warning "Skipping tomb ::1 tomb name::: not indexed." $tombname
         2216             _warning "Run 'tomb index' to create indexes." }
         2217     done
         2218     _message "Search completed."
         2219 }
         2220 
         2221 # }}} - Index and search
         2222 
         2223 # {{{ Resize
         2224 
         2225 # resize tomb file size
         2226 resize_tomb() {
         2227     local tombpath="$1"    # First argument is the path to the tomb
         2228 
         2229     _message "Commanded to resize tomb ::1 tomb name:: to ::2 size:: mebibytes." $1 $OPTS[-s]
         2230 
         2231     [[ -z "$tombpath" ]] && _failure "No tomb name specified for resizing."
         2232     [[ ! -r $tombpath ]] && _failure "Cannot find ::1::" $tombpath
         2233 
         2234     newtombsize="`option_value -s`"
         2235     [[ -z "$newtombsize" ]] && {
         2236         _failure "Aborting operations: new size was not specified, use -s" }
         2237 
         2238     # this also calls _plot()
         2239     is_valid_tomb $tombpath
         2240 
         2241     _load_key # Try loading new key from option -k and set TOMBKEYFILE
         2242 
         2243     local oldtombsize=$(( `stat -c %s "$TOMBPATH" 2>/dev/null` / 1048576 ))
         2244     local mounted_tomb=`mount_list |
         2245         awk -vtomb="[$TOMBNAME]" '/^\/dev\/mapper\/tomb/ { if($7==tomb) print $1 }'`
         2246 
         2247     # Tomb must not be open
         2248     [[ -z "$mounted_tomb" ]] || {
         2249         _failure "Please close the tomb ::1 tomb name:: before trying to resize it." $TOMBNAME }
         2250     # New tomb size must be specified
         2251     [[ -n "$newtombsize" ]] || {
         2252         _failure "You must specify the new size of ::1 tomb name::" $TOMBNAME }
         2253     # New tomb size must be an integer
         2254     [[ $newtombsize == <-> ]] || _failure "Size is not an integer."
         2255 
         2256     # Tombs can only grow in size
         2257     if [[ "$newtombsize" -gt "$oldtombsize" ]]; then
         2258 
         2259         delta="$(( $newtombsize - $oldtombsize ))"
         2260 
         2261         _message "Generating ::1 tomb file:: of ::2 size::MiB" $TOMBFILE $newtombsize
         2262 
         2263         _verbose "Data dump using ::1:: from /dev/urandom" ${DD[1]}
         2264         ${=DD} if=/dev/urandom bs=1048576 count=${delta} >> $TOMBPATH
         2265         [[ $? == 0 ]] || {
         2266             _failure "Error creating the extra resize ::1 size::, operation aborted." \
         2267                      $tmp_resize }
         2268 
         2269     # If same size this allows to re-launch resize if pinentry expires
         2270     # so that it will continue resizing without appending more space.
         2271     # Resizing the partition to the file size cannot harm data anyway.
         2272     elif [[ "$newtombsize" = "$oldtombsize" ]]; then
         2273         _message "Tomb seems resized already, operating filesystem stretch"
         2274     else
         2275         _failure "The new size must be greater then old tomb size."
         2276     fi
         2277 
         2278     { option_is_set --tomb-pwd } && {
         2279         tomb_pwd="`option_value --tomb-pwd`"
         2280         _verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd
         2281         ask_key_password "$tomb_pwd"
         2282     } || {
         2283         ask_key_password
         2284     }
         2285     [[ $? == 0 ]] || _failure "No valid password supplied."
         2286 
         2287     lo_mount "$TOMBPATH"
         2288     nstloop=`lo_new`
         2289 
         2290     mapdate=`date +%s`
         2291     mapper="tomb.$TOMBNAME.$mapdate.$(basename $nstloop)"
         2292 
         2293     _message "opening tomb"
         2294     _cryptsetup luksOpen ${nstloop} ${mapper} || {
         2295         _failure "Failure mounting the encrypted file." }
         2296 
         2297     _sudo cryptsetup resize "${mapper}" || {
         2298         _failure "cryptsetup failed to resize ::1 mapper::" $mapper }
         2299 
         2300     _sudo e2fsck -p -f /dev/mapper/${mapper} || {
         2301         _failure "e2fsck failed to check ::1 mapper::" $mapper }
         2302 
         2303     _sudo resize2fs /dev/mapper/${mapper} || {
         2304         _failure "resize2fs failed to resize ::1 mapper::" $mapper }
         2305 
         2306     # close and free the loop device
         2307     _sudo cryptsetup luksClose "${mapper}"
         2308 
         2309     return 0
         2310 }
         2311 
         2312 # }}}
         2313 
         2314 # {{{ Close
         2315 
         2316 umount_tomb() {
         2317     local tombs how_many_tombs
         2318     local pathmap mapper tombname tombmount loopdev
         2319     local ans pidk pname
         2320 
         2321     if [ "$1" = "all" ]; then
         2322         mounted_tombs=(`list_tomb_mounts`)
         2323     else
         2324         mounted_tombs=(`list_tomb_mounts $1`)
         2325     fi
         2326 
         2327     [[ ${#mounted_tombs} == 0 ]] && {
         2328         _failure "There is no open tomb to be closed." }
         2329 
         2330     [[ ${#mounted_tombs} -gt 1 && -z "$1" ]] && {
         2331         _warning "Too many tombs mounted, please specify one (see tomb list)"
         2332         _warning "or issue the command 'tomb close all' to close them all."
         2333         _failure "Operation aborted." }
         2334 
         2335     for t in ${mounted_tombs}; do
         2336         mapper=`basename ${t[(ws:;:)1]}`
         2337 
         2338         # strip square parens from tombname
         2339         tombname=${t[(ws:;:)5]}
         2340         tombmount=${t[(ws:;:)2]}
         2341         tombfs=${t[(ws:;:)3]}
         2342         tombfsopts=${t[(ws:;:)4]}
         2343         tombloop=${mapper[(ws:.:)4]}
         2344 
         2345         _verbose "Name: ::1 tomb name::" $tombname
         2346         _verbose "Mount: ::1 mount point::" $tombmount
         2347         _verbose "Mapper: ::1 mapper::" $mapper
         2348 
         2349         [[ -e "$mapper" ]] && {
         2350             _warning "Tomb not found: ::1 tomb file::" $1
         2351             _warning "Please specify an existing tomb."
         2352             return 0 }
         2353 
         2354         [[ -n $SLAM ]] && {
         2355             _success "Slamming tomb ::1 tomb name:: mounted on ::2 mount point::" \
         2356                 $tombname $tombmount
         2357             _message "Kill all processes busy inside the tomb."
         2358             { slam_tomb "$tombmount" } || {
         2359                 _failure "Cannot slam the tomb ::1 tomb name::" $tombname }
         2360         } || {
         2361             _message "Closing tomb ::1 tomb name:: mounted on ::2 mount point::" \
         2362                 $tombname $tombmount }
         2363 
         2364         # check if there are binded dirs and close them
         2365         bind_tombs=(`list_tomb_binds $tombname $tombmount`)
         2366         for b in ${bind_tombs}; do
         2367             bind_mapper="${b[(ws:;:)1]}"
         2368             bind_mount="${b[(ws:;:)2]}"
         2369             _message "Closing tomb bind hook: ::1 hook::" $bind_mount
         2370             _sudo umount "`print - ${bind_mount}`" || {
         2371                 [[ -n $SLAM ]] && {
         2372                     _success "Slamming tomb: killing all processes using this hook."
         2373                     slam_tomb "`print - ${bind_mount}`" || _failure "Cannot slam the bind hook ::1 hook::" $bind_mount
         2374                     umount "`print - ${bind_mount}`" || _failure "Cannot slam the bind hook ::1 hook::" $bind_mount
         2375                 } || {
         2376                     _failure "Tomb bind hook ::1 hook:: is busy, cannot close tomb." $bind_mount
         2377                 }
         2378             }
         2379         done
         2380 
         2381         # Execute post-hooks for eventual cleanup
         2382         { option_is_set -n } || {
         2383             exec_safe_post_hooks ${tombmount%%/} close }
         2384 
         2385         _verbose "Performing umount of ::1 mount point::" $tombmount
         2386         _sudo umount ${tombmount}
         2387         [[ $? = 0 ]] || { _failure "Tomb is busy, cannot umount!" }
         2388 
         2389         # If we used a default mountpoint and is now empty, delete it
         2390         tombname_regex=${tombname//\[/}
         2391         tombname_regex=${tombname_regex//\]/}
         2392 
         2393         rmdir $tombmount
         2394 
         2395         _sudo cryptsetup luksClose $mapper
         2396         [[ $? == 0 ]] || {
         2397             _failure "Error occurred in cryptsetup luksClose ::1 mapper::" $mapper }
         2398 
         2399         # Normally the loopback device is detached when unused
         2400         [[ -e "/dev/$tombloop" ]] && _sudo losetup -d "/dev/$tombloop"
         2401         [[ $? = 0 ]] || {
         2402             _verbose "/dev/$tombloop was already closed." }
         2403 
         2404         _success "Tomb ::1 tomb name:: closed: your bones will rest in peace." $tombname
         2405 
         2406     done # loop across mounted tombs
         2407 
         2408     return 0
         2409 }
         2410 
         2411 # Kill all processes using the tomb
         2412 slam_tomb() {
         2413     # $1 = tomb mount point
         2414     if [[ -z `fuser -m "$1" 2>/dev/null` ]]; then
         2415         return 0
         2416     fi
         2417     #Note: shells are NOT killed by INT or TERM, but they are killed by HUP
         2418     for s in TERM HUP KILL; do
         2419         _verbose "Sending ::1:: to processes inside the tomb:" $s
         2420         if option_is_set -D; then
         2421             ps -fp `fuser -m /media/a.tomb 2>/dev/null`|
         2422             while read line; do
         2423                 _verbose $line
         2424             done
         2425         fi
         2426         fuser -s -m "$1" -k -M -$s
         2427         if [[ -z `fuser -m "$1" 2>/dev/null` ]]; then
         2428             return 0
         2429         fi
         2430         if ! option_is_set -f; then
         2431             sleep 3
         2432         fi
         2433     done
         2434     return 1
         2435 }
         2436 
         2437 # }}} - Tomb close
         2438 
         2439 # {{{ Main routine
         2440 
         2441 main() {
         2442 
         2443     _ensure_dependencies  # Check dependencies are present or bail out
         2444 
         2445     local -A subcommands_opts
         2446     ### Options configuration
         2447     #
         2448     # Hi, dear developer!  Are you trying to add a new subcommand, or
         2449     # to add some options?  Well, keep in mind that option names are
         2450     # global: they cannot bear a different meaning or behaviour across
         2451     # subcommands.  The only exception is "-o" which means: "options
         2452     # passed to the local subcommand", and thus can bear a different
         2453     # meaning for different subcommands.
         2454     #
         2455     # For example, "-s" means "size" and accepts one argument. If you
         2456     # are tempted to add an alternate option "-s" (e.g., to mean
         2457     # "silent", and that doesn't accept any argument) DON'T DO IT!
         2458     #
         2459     # There are two reasons for that:
         2460     #    I. Usability; users expect that "-s" is "size"
         2461     #   II. Option parsing WILL EXPLODE if you do this kind of bad
         2462     #       things (it will complain: "option defined more than once")
         2463     #
         2464     # If you want to use the same option in multiple commands then you
         2465     # can only use the non-abbreviated long-option version like:
         2466     # -force and NOT -f
         2467     #
         2468     main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -tmp: U: G: T: -no-color -unsafe)
         2469     subcommands_opts[__default]=""
         2470     # -o in open and mount is used to pass alternate mount options
         2471     subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: "
         2472     subcommands_opts[mount]=${subcommands_opts[open]}
         2473 
         2474     subcommands_opts[create]="" # deprecated, will issue warning
         2475 
         2476     # -o in forge and lock is used to pass an alternate cipher.
         2477     subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom "
         2478     subcommands_opts[dig]="-ignore-swap s: -size=s "
         2479     subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: "
         2480     subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: "
         2481     subcommands_opts[engrave]="k: "
         2482 
         2483     subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: "
         2484     subcommands_opts[close]=""
         2485     subcommands_opts[help]=""
         2486     subcommands_opts[slam]=""
         2487     subcommands_opts[list]="-get-mountpoint "
         2488 
         2489     subcommands_opts[index]=""
         2490     subcommands_opts[search]=""
         2491 
         2492     subcommands_opts[help]=""
         2493     subcommands_opts[bury]="k: -tomb-pwd: "
         2494     subcommands_opts[exhume]="k: -tomb-pwd: "
         2495     # subcommands_opts[decompose]=""
         2496     # subcommands_opts[recompose]=""
         2497     # subcommands_opts[install]=""
         2498     subcommands_opts[askpass]=""
         2499     subcommands_opts[source]=""
         2500     subcommands_opts[resize]="-ignore-swap s: -size=s k: -tomb-pwd: "
         2501     subcommands_opts[check]="-ignore-swap "
         2502     #    subcommands_opts[translate]=""
         2503 
         2504     ### Detect subcommand
         2505     local -aU every_opts #every_opts behave like a set; that is, an array with unique elements
         2506     for optspec in $subcommands_opts$main_opts; do
         2507         for opt in ${=optspec}; do
         2508             every_opts+=${opt}
         2509         done
         2510     done
         2511     local -a oldstar
         2512     oldstar=("${(@)argv}")
         2513     #### detect early: useful for --option-parsing
         2514     zparseopts -M -D -Adiscardme ${every_opts}
         2515     if [[ -n ${(k)discardme[--option-parsing]} ]]; then
         2516         print $1
         2517         if [[ -n "$1" ]]; then
         2518             return 1
         2519         fi
         2520         return 0
         2521     fi
         2522     unset discardme
         2523     if ! zparseopts -M -E -D -Adiscardme ${every_opts}; then
         2524         _failure "Error parsing."
         2525         return 127
         2526     fi
         2527     unset discardme
         2528     subcommand=$1
         2529     if [[ -z $subcommand ]]; then
         2530         subcommand="__default"
         2531     fi
         2532 
         2533     if [[ -z ${(k)subcommands_opts[$subcommand]} ]]; then
         2534         _warning "There's no such command \"::1 subcommand::\"." $subcommand
         2535         exitv=127 _failure "Please try -h for help."
         2536     fi
         2537     argv=("${(@)oldstar}")
         2538     unset oldstar
         2539 
         2540     ### Parsing global + command-specific options
         2541     # zsh magic: ${=string} will split to multiple arguments when spaces occur
         2542     set -A cmd_opts ${main_opts} ${=subcommands_opts[$subcommand]}
         2543     # if there is no option, we don't need parsing
         2544     if [[ -n $cmd_opts ]]; then
         2545         zparseopts -M -E -D -AOPTS ${cmd_opts}
         2546         if [[ $? != 0 ]]; then
         2547             _warning "Some error occurred during option processing."
         2548             exitv=127 _failure "See \"tomb help\" for more info."
         2549         fi
         2550     fi
         2551     #build PARAM (array of arguments) and check if there are unrecognized options
         2552     ok=0
         2553     PARAM=()
         2554     for arg in $*; do
         2555         if [[ $arg == '--' || $arg == '-' ]]; then
         2556             ok=1
         2557             continue #it shouldn't be appended to PARAM
         2558         elif [[ $arg[1] == '-'  ]]; then
         2559             if [[ $ok == 0 ]]; then
         2560                 exitv=127 _failure "Unrecognized option ::1 arg:: for subcommand ::2 subcommand::" $arg $subcommand
         2561             fi
         2562         fi
         2563         PARAM+=$arg
         2564     done
         2565     # First parameter actually is the subcommand: delete it and shift
         2566     [[ $subcommand != '__default' ]] && { PARAM[1]=(); shift }
         2567 
         2568     ### End parsing command-specific options
         2569 
         2570     # Use colors unless told not to
         2571     { ! option_is_set --no-color } && { autoload -Uz colors && colors }
         2572     # Some options are only available during insecure mode
         2573     { ! option_is_set --unsafe } && {
         2574         for opt in --tomb-pwd --use-urandom --tomb-old-pwd; do
         2575             { option_is_set $opt } && {
         2576                 exitv=127 _failure "You specified option ::1 option::, which is DANGEROUS and should only be used for testing\nIf you really want so, add --unsafe" $opt }
         2577         done
         2578     }
         2579     # read -t or --tmp flags to set a custom temporary directory
         2580     option_is_set --tmp && TMPPREFIX=$(option_value --tmp)
         2581 
         2582 
         2583     # When we run as root, we remember the original uid:gid to set
         2584     # permissions for the calling user and drop privileges
         2585 
         2586     [[ "$PARAM" == "" ]] && {
         2587         _verbose "Tomb command: ::1 subcommand::" $subcommand
         2588     } || {
         2589         _verbose "Tomb command: ::1 subcommand:: ::2 param::" $subcommand $PARAM
         2590     }
         2591 
         2592     [[ -z $_UID ]] || {
         2593         _verbose "Caller: uid[::1 uid::], gid[::2 gid::], tty[::3 tty::]." \
         2594             $_UID $_GID $_TTY
         2595     }
         2596 
         2597     _verbose "Temporary directory: $TMPPREFIX"
         2598 
         2599     # Process subcommand
         2600     case "$subcommand" in
         2601 
         2602         # USAGE
         2603         help)
         2604             usage
         2605             ;;
         2606 
         2607         # DEPRECATION notice (leave here as 'create' is still present in old docs)
         2608         create)
         2609             _warning "The create command is deprecated, please use dig, forge and lock instead."
         2610             _warning "For more informations see Tomb's manual page (man tomb)."
         2611             _failure "Operation aborted."
         2612             ;;
         2613 
         2614         # CREATE Step 1: dig -s NN file.tomb
         2615         dig)
         2616             dig_tomb ${=PARAM}
         2617             ;;
         2618 
         2619         # CREATE Step 2: forge file.tomb.key
         2620         forge)
         2621             forge_key ${=PARAM}
         2622             ;;
         2623 
         2624         # CREATE Step 2: lock -k file.tomb.key file.tomb
         2625         lock)
         2626             lock_tomb_with_key ${=PARAM}
         2627             ;;
         2628 
         2629         # Open the tomb
         2630         mount|open)
         2631             mount_tomb ${=PARAM}
         2632             ;;
         2633 
         2634         # Close the tomb
         2635         # `slam` is used to force closing.
         2636         umount|close|slam)
         2637             [[ "$subcommand" ==  "slam" ]] && SLAM=1
         2638             umount_tomb $PARAM[1]
         2639             ;;
         2640 
         2641         # Grow tomb's size
         2642         resize)
         2643             [[ $RESIZER == 0 ]] && {
         2644                 _failure "Resize2fs not installed: cannot resize tombs." }
         2645             resize_tomb $PARAM[1]
         2646             ;;
         2647 
         2648         ## Contents manipulation
         2649 
         2650         # Index tomb contents
         2651         index)
         2652             index_tombs $PARAM[1]
         2653             ;;
         2654 
         2655         # List tombs
         2656         list)
         2657             list_tombs $PARAM[1]
         2658             ;;
         2659 
         2660         # Search tomb contents
         2661         search)
         2662             search_tombs ${=PARAM}
         2663             ;;
         2664 
         2665         ## Locking operations
         2666 
         2667         # Export key to QR Code
         2668         engrave)
         2669             [[ $QRENCODE == 0 ]] && {
         2670                 _failure "QREncode not installed: cannot engrave keys on paper." }
         2671             engrave_key ${=PARAM}
         2672             ;;
         2673 
         2674         # Change password on existing key
         2675         passwd)
         2676             change_passwd $PARAM[1]
         2677             ;;
         2678 
         2679         # Change tomb key
         2680         setkey)
         2681             change_tomb_key ${=PARAM}
         2682             ;;
         2683 
         2684         # STEGANOGRAPHY: hide key inside an image
         2685         bury)
         2686             [[ $STEGHIDE == 0 ]] && {
         2687                 _failure "Steghide not installed: cannot bury keys into images." }
         2688             bury_key $PARAM[1]
         2689             ;;
         2690 
         2691         # STEGANOGRAPHY: read key hidden in an image
         2692         exhume)
         2693             [[ $STEGHIDE == 0 ]] && {
         2694                 _failure "Steghide not installed: cannot exhume keys from images." }
         2695             exhume_key $PARAM[1]
         2696             ;;
         2697 
         2698         ## Internal commands useful to developers
         2699 
         2700         # Make tomb functions available to the calling shell or script
         2701         'source')   return 0 ;;
         2702 
         2703         # Ask user for a password interactively
         2704         askpass)    ask_password $PARAM[1] $PARAM[2] ;;
         2705 
         2706         # Default operation: presentation, or version information with -v
         2707         __default)
         2708             _print "Tomb ::1 version:: - a strong and gentle undertaker for your secrets" $VERSION
         2709             _print "\000"
         2710             _print " Copyright (C) 2007-2015 Dyne.org Foundation, License GNU GPL v3+"
         2711             _print " This is free software: you are free to change and redistribute it"
         2712             _print " For the latest sourcecode go to <http://dyne.org/software/tomb>"
         2713             _print "\000"
         2714             option_is_set -v && {
         2715                 local langwas=$LANG
         2716                 LANG=en
         2717                 _print " This source code is distributed in the hope that it will be useful,"
         2718                 _print " but WITHOUT ANY WARRANTY; without even the implied warranty of"
         2719                 _print " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
         2720                 LANG=$langwas
         2721                 _print " When in need please refer to <http://dyne.org/support>."
         2722                 _print "\000"
         2723                 _print "System utils:"
         2724                 _print "\000"
         2725                 cat <<EOF
         2726   `sudo -V | head -n1`
         2727   `cryptsetup --version`
         2728   `pinentry --version`
         2729   `gpg --version | head -n1` - key forging algorithms (GnuPG symmetric ciphers):
         2730   `list_gnupg_ciphers`
         2731 EOF
         2732                 _print "\000"
         2733                 _print "Optional utils:"
         2734                 _print "\000"
         2735                 _list_optional_tools version
         2736                 return 0
         2737             }
         2738             usage
         2739             ;;
         2740 
         2741         # Reject unknown command and suggest help
         2742         *)
         2743             _warning "Command \"::1 subcommand::\" not recognized." $subcommand
         2744             _message "Try -h for help."
         2745             return 1
         2746             ;;
         2747     esac
         2748     return $?
         2749 }
         2750 
         2751 # }}}
         2752 
         2753 # {{{ Run
         2754 
         2755 main "$@" || exit $?   # Prevent `source tomb source` from exiting
         2756 
         2757 # }}}
         2758 
         2759 # -*- tab-width: 4; indent-tabs-mode:nil; -*-
         2760 # vim: set shiftwidth=4 expandtab: