#!/bin/zsh # # Tomb, the Crypto Undertaker # # a tool to easily operate file encryption of private and secret data # # Copyleft (C) 2007-2011 Denis Roio # # This source code is free software; you can redistribute it and/or # modify it under the terms of the GNU Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This source code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # Please refer to the GNU Public License for more details. # # You should have received a copy of the GNU Public License along with # this source code; if not, write to: # Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. VERSION=1.0 DATE=Feb/2011 # PATH=/usr/bin:/usr/sbin:/bin:/sbin # standard output message routines # it's always useful to wrap them, in case we change behaviour later notice() { if ! [ $QUIET ]; then echo "[*] $1" >&2; fi } act() { if ! [ $QUIET ]; then echo " . $1" >&2; fi } error() { if ! [ $QUIET ]; then echo "[!] $1" >&2; fi } func() { if [ $DEBUG ]; then echo "[D] $1" >&2; fi } # which dd command to use which dcfldd > /dev/null if [ $? = 0 ]; then DD="dcfldd" else DD=dd fi # which wipe command to use which wipe > /dev/null if [ $? = 0 ]; then WIPE=(wipe -f -s -q) else WIPE=(rm -f) fi # we use pinentry now # comes from gpg project and is much more secure # it also conveniently uses the right toolkit ask_password() { # pinentry has no custom icon setting # so we need to temporary modify the gtk theme if [ -r ~/.gtkrc-2.0 ]; then cp ~/.gtkrc-2.0 ~/.gtkrc-2.0.tomb.bak else touch ~/.gtkrc-2.0 fi cat <> ~/.gtkrc-2.0 pixmap_path "/usr/local/share/pixmaps" style "normal" { stock["gtk-dialog-authentication"] = {{"monmort.xpm"}} } widget "*" style "normal" EOF cat < /dev/null if [ $? = 0 ]; then sudo -u $SUDO_USER "${@[@]}" return $? else error "Tomb requires sudo. please install it." return 1 fi } # escalate privileges check_priv() { id | grep root > /dev/null if [ $? != 0 ]; then which sudo > /dev/null if [ $? != 0 ]; then error "Tomb requires sudo. please install it." exit 1 fi func "Using sudo for root execution of 'tomb ${(f)ARGS}'" # check if sudo has a timestamp active sudok=false sudo -n tomb 2> /dev/null if [ $? != 0 ]; then # if not then ask a password cat <&1 >/dev/null if [ $? != 0 ]; then error "$arg is not a valid tomb file, operation aborted" return 1 fi tombname=${tombfile%%\.*} act "tomb found: ${tombdir}/${tombfile}" # now check if the key is kept beside or in args # we use the extension .key # the problem with .tomb.gpg is that decoding by hand using gpg it # can override the tomb contents if the key is in the same # directory than the tomb if [ $KEY ]; then tombkey=$KEY # commandline -k flag act "tomb key specified manually: $tombkey" elif [ -r ${tombdir}/${tombname}.tomb.key ]; then tombkey=${tombdir}/${tombname}.tomb.key act "key found for tomb '${tombname}': ${tombkey}" else error "key not found for tomb '${tombname}'" return 1 fi return 0 } ############################ ### main() ### echo $@ | grep '\-D' 2>&1 > /dev/null if [ $? = 0 ]; then fi ARGS=$@[@] OPTS=`getopt -o hvqDs:k:n -n 'tomb' -- "$@"` while true; do case "$1" in -h) cat < Syntax: tomb [options] command [file] [place] Commands: create create a new tomb FILE and its keys open open an existing tomb FILE on PLACE close closes the tomb open on PLACE bury hide a tomb key FILE inside a jpeg PLACE exhume extract a tomb key FILE from a jpeg PLACE Options: -s size of the tomb file when creating one (in MB) -k path to the key to use for opening a tomb -n don't process the hooks found in tomb -h print this help -v version information for this tool -q run quietly without printing informations -D print debugging information at runtime For more informations on Tomb read the manual: man tomb Please report bugs on . EOF exit 0 ;; -v) notice "Tomb - simple commandline tool for encrypted storage" act "version $VERSION ($DATE) by Jaromil @ dyne.org" # print out the GPL license in this file act "" cat $0 | awk ' BEGIN { license=0 } /^# This source/ { license=1 } { if(license==1) print " " $0 } /MA 02139, USA.$/ { license=0 } ' act "" exit 0 ;; -q) QUIET=1; shift 1 ;; -D) echo "[D] Tomb invoked with args \"${(f)@}\" " echo "[D] running on `date`" DEBUG=1; shift 1 ;; -s) SIZE=$2; shift 2 ;; -k) KEY=$2; shift 2 ;; -b) NOBIND=1; shift 1 ;; --) shift; break ;; *) CMD=$1; FILE=$2; MOUNT=$3; # compat with old args CMD2=${2}; CMD3=${3}; break ;; esac done if ! [ $CMD ]; then error "first argument missing, use -h for help" exit 0 fi func "Tomb command: $CMD $CMD2 $CMD3" create_tomb() { if ! [ ${CMD2} ]; then error "no tomb name specified for creation" return 1 fi tombfile=`basename ${CMD2}` tombdir=`dirname ${CMD2}` # make sure the file has a .tomb extension tombname=${tombfile%%\.*} tombfile=${tombname}.tomb if [ -e ${tombdir}/${tombfile} ]; then error "tomb exists already. I'm not digging here:" ls -lh ${tombdir}/${tombfile} return 1 fi notice "Creating a new tomb in ${tombdir}/${tombfile}" if [ -z $SIZE ]; then if [ $CMD3 ]; then tombsize=${CMD3} else act "No size specified, summoning the Tomb Undertaker to guide us in the creation." tomb-open &! return 0 fi else tombsize=${SIZE} fi tombsize_4k=`expr $tombsize \* 1000 / 4` act "Generating ${tombfile} of ${tombsize}Mb (${tombsize_4k} blocks of 4Kb)" $DD if=/dev/urandom bs=4k count=${tombsize_4k} of=${tombdir}/${tombfile} if [ $? = 0 -a -e ${tombdir}/${tombfile} ]; then act "OK: `ls -lh ${tombdir}/${tombfile}`" else error "Error creating the tomb ${tombdir}/${tombfile}, operation aborted." exit 1 fi nstloop=`losetup -f` # get the number for next loopback device losetup -f ${tombdir}/${tombfile} # allocates the next loopback for our file # create the keyfile in tmpfs so that we leave less traces in RAM keytmp=`tempfile -p tomb` rm -f $keytmp mkdir -p $keytmp mount tmpfs ${keytmp} -t tmpfs -o size=1m if [ $? != 0 ]; then error "cannot mount tmpfs filesystem in volatile memory" error "operation aborted." losetup -d $nstloop rm -r $keytmp exit 1 fi act "Generating secret key..." act "this operation takes time, keep using this computer on other tasks," act "once done you will be asked to choose a password for your tomb." touch ${keytmp}/tomb.tmp chmod 0600 ${keytmp}/tomb.tmp $DD bs=1 count=256 if=/dev/random of=${keytmp}/tomb.tmp if ! [ -r ${keytmp}/tomb.tmp ]; then error "cannot generate encryption key, operation aborted." umount ${keytmp} losetup -d $nstloop rm -r $keytmp exit 1 fi notice "Setup your secret key file ${tombname}.tomb.key" # here user is prompted for key password for c in 1 2 3; do # 3 tries to write two times a matching password tombpass=`exec_as_user tomb askpass ${tombname}` tombpasstmp=$tombpass tombpass=`exec_as_user tomb askpass "${tombname} (again)"` if [ "$tombpasstmp" = "$tombpass" ]; then break; fi unset tombpasstmp unset tombpass done if [ -z $tombpass ]; then error "passwords don't match, aborting operation" umount ${keytmp} losetup -d $nstloop rm -r $keytmp exit 1 fi echo "${tombpass}" | gpg \ --openpgp --batch --no-options --no-tty --passphrase-fd 0 2>/dev/null \ -o "${tombdir}/${tombname}.tomb.key" -c -a ${keytmp}/tomb.tmp # if [ $? != 0 ]; then # error "setting password failed: gnupg returns 2" # umount ${keytmp} # losetup -d $nstloop # rm -r $keytmp # exit 1 # fi act "formatting Luks mapped device" # we use aes-cbc-essiv with sha256 # for security, performance and compatibility cryptsetup --batch-mode \ --cipher aes-cbc-essiv:sha256 --key-size 256 \ luksFormat ${nstloop} ${keytmp}/tomb.tmp if ! [ $? = 0 ]; then act "operation aborted." exit 0 fi cryptsetup --key-file ${keytmp}/tomb.tmp --cipher aes luksOpen ${nstloop} tomb.tmp ${WIPE[@]} ${keytmp}/tomb.tmp umount ${keytmp} rm -r ${keytmp} # cryptsetup luksDump ${nstloop} act "formatting your Tomb with Ext4 filesystem" mkfs.ext4 -q -F -j -L ${tombname} /dev/mapper/tomb.tmp if [ $? != 0 ]; then error "Tomb format returns error" error "your tomb ${tombfile} maybe corrupt" fi sync cryptsetup luksClose tomb.tmp losetup -d ${nstloop} act "done creating $tombname encrypted storage (using Luks dm-crypt AES/SHA256)" notice "Your tomb is ready in ${tombdir}/${tombfile} and secured with key ${tombname}.tomb.key" } mount_tomb() { get_arg_tomb $CMD2 if [ $? != 0 ]; then error "operation aborted." return 1 fi if ! [ $CMD3 ]; then tombmount=/media/${tombfile} act "mountpoint not specified, using default: $tombmount" elif ! [ -x $CMD3 ]; then error "mountpoint $CMD3 doesn't exist, operation aborted." if [ -n "$usbkey_mount" ]; then umount $usbkey_mount rmdir $usbkey_mount unset usbkey_mount fi return 1 else tombmount=$CMD3 fi notice "mounting $tombfile on mountpoint $tombmount" # we need root from here on mkdir -p $tombmount nstloop=`losetup -f` losetup -f ${tombdir}/${tombfile} act "check for a valid LUKS encrypted device" cryptsetup isLuks ${nstloop} if [ $? != 0 ]; then # is it a LUKS encrypted nest? see cryptsetup(1) error "$tombfile is not a valid Luks encrypted storage file" $norm || rmdir $tombmount 2>/dev/null return 1 fi # save date of mount in minutes since 1970 mapdate="`date +%s`" mapdate="`echo ${mapdate}/60 | bc -l | cut -d. -f1`" mapper="tomb.${tombname}.${mapdate}.`basename $nstloop`" keyname=`basename $tombkey | cut -d. -f1` notice "Password is required for key ${keyname}" for c in 1 2 3; do if [ $c = 1 ]; then tombpass=`exec_as_user tomb askpass ${keyname}` else tombpass=`exec_as_user tomb askpass "$keyname (retry $c)"` fi echo "${tombpass}" \ | gpg --batch --passphrase-fd 0 --no-tty --no-options \ -d "${tombkey}" 2> /dev/null \ | cryptsetup --key-file - luksOpen ${nstloop} ${mapper} unset tombpass if [ -r /dev/mapper/${mapper} ]; then break; # password was correct fi done if ! [ -r /dev/mapper/${mapper} ]; then error "failure mounting the encrypted file" losetup -d ${nstloop} $norm || rmdir ${tombmount} 2>/dev/null return 1 fi act "encrypted storage filesystem check" fsck -p -C0 /dev/mapper/${mapper} act "tomb engraved as $tombname" tune2fs -L ${tombname} /dev/mapper/${mapper} > /dev/null mount -o rw,noatime,nodev /dev/mapper/${mapper} ${tombmount} # Ensure the user can write the disk - 10x Hellekin :) ME=${SUDO_USER:-$(whoami)} chmod 0750 ${tombmount} chown $(id -u $ME):$(id -g $ME) ${tombmount} notice "encrypted storage $tombfile succesfully mounted on $tombmount" # exec_bind_hooks ${tombmount} if ! [ $NOBIND ]; then exec_safe_bind_hooks ${tombmount} exec_post_hooks ${tombmount} open fi return 0 } encode_key() { tombkey=$CMD2 imagefile=$CMD3 file $tombkey | grep PGP > /dev/null if [ $? != 0 ]; then error "encode failed: $tombkey is not a tomb key" return 1 fi file $imagefile | grep JPEG > /dev/null if [ $? != 0 ]; then error "encode failed: $imagefile is not a jpeg image" return 1 fi notice "Encoding key $tombkey inside image $imagefile" act "please choose a password for the encoding" # here user is prompted for key password for c in 1 2 3; do # 3 tries to write two times a matching password tombpass=`exec_as_user tomb askpass ${tombkey}` tombpasstmp=$tombpass tombpass=`exec_as_user tomb askpass "${tombkey} (again)"` if [ "$tombpasstmp" = "$tombpass" ]; then break; fi unset tombpasstmp unset tombpass done if [ -z $tombpass ]; then error "passwords don't match, aborting operation." return 1 fi awk ' /^-----/ {next} /^Version/ {next} {print $0}' ${tombkey} \ | steghide embed --embedfile - --coverfile ${imagefile} \ -p ${tombpass} -z 9 -e serpent cbc if [ $? != 0 ]; then error "encoding error: steghide reports problems" res=1 else notice "tomb key encoded succesfully into image ${imagefile}" res=0 fi unset tombpass return $res } decode_key() { tombname=$CMD2 imagefile=$CMD3 res=1 file $imagefile | grep JPEG > /dev/null if [ $? != 0 ]; then error "encode failed: $imagefile is not a jpeg image" return 1 fi keyfile=${tombname%%\.*}.tomb.key notice "Trying to exhume a key out of image $imagefile" for c in 1 2 3; do if [ $c = 1 ]; then tombpass=`exec_as_user tomb askpass ${keyfile}` else tombpass=`exec_as_user tomb askpass "$keyfile (retry $c)"` fi steghide extract -sf ${imagefile} -p ${tombpass} -xf - \ | awk ' BEGIN { print "-----BEGIN PGP MESSAGE-----" } { print $0 } END { print "-----END PGP MESSAGE-----" }' > ${keyfile} if [ "`cat ${keyfile} | wc -l`" != "3" ]; then act "${keyfile} succesfully decoded" res=0 break; fi done unset tombpass if [ $res != 0 ]; then error "nothing found." fi return $res } exec_bind_hooks() { mnt=$1 # first argument is where the tomb is mounted if ! [ -r ${mnt}/bind-hooks ]; then return; fi # if 'bind-hooks' is found inside the tomb, parse it # every line contains two strings: # the first is a directory existing inside the tomb # the second is the place where it should be mounted (-o bind) hook=`cat ${mnt}/bind-hooks | awk ' /^#/ { next } { if($1 && $2) print "mount -o bind \${mnt}/" $1 " " $2 "; " } '` # restore $HOME for the calling user ME=${SUDO_USER:-$(whoami)} HOME=$(grep $ME /etc/passwd | sed "s/^${ME}:.*:.*:.*:.*:\([\/a-z]*\):.*$/\1/" 2>/dev/null) act "bind hooks found, mounting directories as requested" # execute the mount commands eval $hook } # FIXME: this should sanitize pathes! exec_safe_bind_hooks() { local MOUNTPOINT="${1}" local ME=${SUDO_USER:-$(whoami)} local HOME=$(grep $ME /etc/passwd | sed "s/^${ME}:.*:.*:.*:.*:\([\/a-z]*\):.*$/\1/" 2>/dev/null) if [ $? -ne 0 ]; then error "how pitiful! A tomb, and no HOME" return 1 fi if [ -z "$MOUNTPOINT" -o ! -d "$MOUNTPOINT" ]; then error "cannot exec bind hooks without a mounted tomb." return 1 fi if ! [ -r "$MOUNTPOINT/bind-hooks" ]; then func "bind-hooks not found in $MOUNTPOINT" return 1 fi typeset -al created typeset -al mounted typeset -Al maps maps=($(<"$MOUNTPOINT/bind-hooks")) for dir in ${(k)maps}; do if [ "${dir[1]}" = "/" -o "${dir[1,2]}" = ".." ]; then error "bind-hooks map format: local/to/tomb local/to/\$HOME" continue fi if [ "${${maps[$dir]}[1]}" = "/" -o "${${maps[$dir]}[1,2]}" = ".." ]; then error "bind-hooks map format: local/to/tomb local/to/\$HOME. Rolling back" for dir in ${mounted}; do umount $dir; done for dir in ${created}; do rmdir $dir; done return 1 fi if [ ! -d "$HOME/${maps[$dir]}" ]; then act "creating $HOME/${maps[$dir]}" mkdir -p $HOME/${maps[$dir]} created+=("$HOME/${maps[$dir]}") fi mount --bind $MOUNTPOINT/$dir $HOME/${maps[$dir]} mounted+=("$HOME/${maps[$dir]}") done } exec_post_hooks() { mnt=$1 # first argument is where the tomb is mounted if ! [ -x ${mnt}/post-hooks ]; then return; fi # if 'post-hooks' is found inside the tomb, check it: if it is an # executable, launch it as a user this might need a dialog for # security on what is being run, however we expect you know well # what is inside your tomb. this feature opens the possibility to # make encrypted executables. cat ${mnt}/post-hooks | head -n1 | grep '^#!/' if [ $? = 0 ]; then act "post hooks found, executing as user $SUDO_USER" exec_as_user ${mnt}/post-hooks $2 fi } backup_tomb() { # FIXME - duplicity asks passwords too often # using duplicity which duplicity > /dev/null if [ $? != 0 ]; then error "duplicity not found, can't operate backup" return 1 fi if [ -z $CMD3 ]; then error "backup command needs 2 arguments: tomb and destination url" error "please refer to tomb(1) and duplicity(1) manuals for more information" return 1 fi # is it a tomb? get_arg_tomb ${CMD2} if [ $? != 0 ]; then error "there is no tomb to backup, operation aborted." return 1 fi # is it a url? echo "${CMD3}" | grep -i -e '^.*:\/\/.*' 2>&1 > /dev/null if ! [ $? = 0 ]; then error "second argument is not a valid duplicity url." error "read the tomb(1) and duplicity(1) manual for more information" return 1 fi bckurl=${CMD3} # is it ssh? protocol="`expr substr $bckurl 1 3`" act "backup over protocol $protocol" if [ "$protocol" = "ssh" ]; then act "ssh connection requires a password" FTP_PASSWORD="`exec_as_user tomb askpass $bckurl`" dupopts="--ssh-askpass" # TODO verify ssh access before duplicity does # since it blocks the thing retrying 5 times and such crap # i.e. try ssh true to sshurl="`echo $bckurl | sed -e 's/ssh:\/\///'`" # --no-print-statistics fi # duplicity works only on directories # so we create a directory in tmpfs and bind the tomb inside it # during backup the encrypted tomb will be exposed # TODO: check that the tomb is not mounted and, if mounted # remount it read-only so it doesn't gets modified during bck bckname=${tombname}.bck mkdir -p /dev/shm/${bckname} if [ $? != 0 ]; then error "cannot generate a temporary backup directory in /dev/shm, operation aborted." return 1 fi bcktmpdir=/dev/shm/${bckname} # mmm, maybe we should mount our own tmpfs? we need root anyway for mount -o bind # if we reach to eliminate this mount trick and upload only one file with duplicity # then this function doesn't needs to be root to work. touch ${bcktmpdir}/${tombfile} mount -o bind ${tombdir}/${tombfile} ${bcktmpdir}/${tombfile} bcklast=`exec_as_user duplicity \ ${(s: :)dupopts} \ collection-status ${bckurl} \ | awk '/^Last full backup date:/ { print $5 }'` # we detect if backup already exists or not so we can handle # password prompt (choosing a password for full, inserting for incr) if [ "$bcklast" = "none" ]; then notice "Creating a backup of tomb $tombname on url $bckurl" exec_as_user FTP_PASSWORD="$FTP_PASSWORD" duplicity ${(s: :)dupopts} \ full ${bcktmpdir} ${bckurl} else notice "Updating a backup of tomb $tombname on url $bckurl" exec_as_user FTP_PASSWORD="$FTP_PASSWORD" duplicity ${(s: :)dupopts} \ incr ${bcktmpdir} ${bckurl} fi unset FTP_PASSWORD if [ $? != 0 ]; then error "duplicity reported error, operation aborted" umount ${bcktmpdir}/${tombfile} return 1 fi notice "Operation successful." umount ${bcktmpdir}/${tombfile} return 0 } umount_tomb() { if ! [ $1 ]; then how_many_tombs="`find /dev/mapper -name 'tomb.*' | wc -w`" if [ "$how_many_tombs" = "0" ]; then error "there is no open tomb to be closed" return 1 elif [ "$how_many_tombs" = "1" ]; then mapper=`find /dev/mapper -name 'tomb.*'` else error "too many tombs mounted, please specify which to unmount:" ls /dev/mapper/tomb.* error "or issue the command 'tomb close all' to clos'em all." return 1 fi fi if [ "$1" = "all" ]; then tombs=`find /dev/mapper -name 'tomb.*'` if ! [ $tombs ]; then notice "Tombs are all closed, cemetery is quiet." return 0 fi for t in ${(f)tombs}; do umount_tomb ${t} done return 0 fi if [ -r "$1" ]; then # accepts relative and absolute path mapper="$1" elif [ -r /dev/mapper/${1} ]; then mapper=/dev/mapper/${1} else error "tomb not found: $1" error "please specify an existing /dev/mapper/tomb.*" return 0 fi basemap=`basename $mapper` tombname=`echo ${basemap} | cut -d. -f2` tombmount=`mount | grep $mapper | awk '{print $3}'` # check if there are binded dirs and close them first mount | grep "${tombmount}" 2>/dev/null | grep -v loop 2>&1 > /dev/null if [ $? = 0 ]; then act "closing bind hooks for tomb $tombname " unbind=`mount | grep ${tombmount} | grep -v loop | awk ' { print "umount " $3 "; " } '` eval $unbind func "umount binded dirs:" func "$unbind" fi # Execute post-hooks for eventual cleanup if ! [ $NOBIND ]; then exec_post_hooks ${tombmount} close fi act "closing tomb $tombname on dm-crypt $basemap" mount | grep $mapper 2>&1 >/dev/null if [ $? = 0 ]; then # still mounted umount ${mapper} if ! [ $? = 0 ]; then # TODO: ask user if wanting to SLAM the tomb closed # then kill all processes found using it with fuser and lsof return 1 fi fi cryptsetup luksClose $basemap if ! [ $? = 0 ]; then error "error occurred in cryptsetup luksClose ${basemap}" return 1 fi losetup -d "/dev/`echo $basemap | cut -d. -f4`" notice "Tomb $tombname closed: your bones will rest in peace." return 0 } # install mime-types, bells and whistles for the desktop # see http://developers.sun.com/solaris/articles/integrating_gnome.html # and freedesktop specs install_tomb() { # TODO: distro package deps (for binary) # debian: zsh, cryptsetup, sudo act "updating mimetypes..." cat < /tmp/dyne-tomb.xml Tomb encrypted volume Tomb crypto key EOF xdg-mime install /tmp/dyne-tomb.xml xdg-icon-resource install --context mimetypes --size 32 monmort.xpm monmort xdg-icon-resource install --size 32 monmort.xpm dyne-monmort rm /tmp/dyne-tomb.xml act "updating desktop..." cat < /usr/share/applications/tomb.desktop [Desktop Entry] Version=1.0 Type=Application Name=Tomb crypto undertaker GenericName=Crypto undertaker Comment=Keep your bones safe Exec=tomb-open %U TryExec=tomb-open Icon=monmort.xpm Terminal=true Categories=Utility;Security;Archiving;Filesystem; MimeType=application/x-tomb-volume; X-AppInstall-Package=tomb EOF update-desktop-database act "updating menus..." cat < /etc/menu/tomb ?package(tomb):command="tomb" icon="/usr/share/pixmaps/monmort.xpm" needs="text" \ section="Applications/Accessories" title="Tomb" hints="Crypto" \ hotkey="Tomb" EOF update-menus act "updating mime info..." cat < /usr/share/mime-info/tomb.keys # actions for encrypted tomb storage application/x-tomb-volume: open=tomb-open %f view=tomb-open %f icon-filename=monmort.xpm short_list_application_ids_for_novice_user_level=tomb EOF cat < /usr/share/mime-info/tomb.mime # mime type for encrypted tomb storage application/x-tomb-volume ext: tomb application/x-tomb-key ext: tomb.key EOF cat < /usr/lib/mime/packages/tomb application/x-tomb-volume; tomb-open '%s'; priority=8 EOF update-mime act "updating application entry..." cat < /usr/share/application-registry/tomb.applications tomb command=tomb-open name=Tomb - Crypto Undertaker can_open_multiple_files=false expects_uris=false requires_terminal=true mime-types=application/x-tomb-volume,application/x-tomb-key EOF act "Tomb is now installed." } case "$CMD" in create) check_priv ; create_tomb ;; mount) check_priv ; mount_tomb ;; open) check_priv ; mount_tomb ;; umount) check_priv ; umount_tomb ${CMD2} ;; unmount) check_priv ; umount_tomb ${CMD2} ;; close) check_priv ; umount_tomb ${CMD2} ;; bury) encode_key ${CMD2} ${CMD3} ;; exhume) decode_key ${CMD2} ;; backup) check_priv ; backup_tomb ${CMD2} ${CMD3} ;; install) check_priv ; install_tomb ;; askpass) ask_password $CMD2 ;; status) tomb-status ;; *) error "command \"$CMD\" not recognized" act "try -h for help" return 1 ;; esac # return codes from called functions # return $?