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