"Fossies" - the Fresh Open Source Software Archive

Member "Tomb-2.7/extras/android/tomb" (11 Oct 2019, 91671 Bytes) of package /linux/privat/Tomb-2.7.tar.gz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "tomb": 2.5_vs_2.6.

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