URI: 
       t[cleanup] Introduce _whoami ; clean ; pass all tests with or without sudo - tomb - the crypto undertaker
  HTML git clone git://parazyd.org/tomb.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit cd1ceac92e0a3ec32922f676b9c2ab245235cf1d
   DIR parent b0538983000b6c6f23fee119a0b900a4a7a7746c
  HTML Author: hellekin <hellekin@cepheide.org>
       Date:   Thu, 23 Oct 2014 22:48:05 -0300
       
       t[cleanup] Introduce _whoami ; clean ; pass all tests with or without sudo
       
       Diffstat:
         M tomb                                |     320 ++++++++++++++++++++-----------
       
       1 file changed, 210 insertions(+), 110 deletions(-)
       ---
   DIR diff --git a/tomb b/tomb
       t@@ -37,9 +37,15 @@
        
        # {{{ Global variables
        
       -VERSION=1.6
       -DATE="Sept/2014"
       -TOMBEXEC=$0
       +typeset -r VERSION="1.7"
       +typeset -r DATE="Oct/2014"
       +typeset -r TOMBEXEC=$0
       +
       +# Tomb is using some global variables set by the shell:
       +# TMPPREFIX, UID, GID, PATH, TTY, USERNAME
       +# You can grep 'global variable' to see where they are used.
       +
       +# Keep a reference of the original command line arguments
        typeset -a OLDARGS
        for arg in ${argv}; do OLDARGS+=($arg); done
        
       t@@ -49,7 +55,7 @@ DD=(dd)
        WIPE=(rm -f)
        MKFS=(mkfs.ext3 -q -F -j -L)
        
       -# Flag optional commands if available
       +# Flag optional commands if available (see _ensure_dependencies())
        typeset -i 2 KDF=1
        typeset -i 2 STEGHIDE=1
        typeset -i 2 RESIZER=1
       t@@ -57,20 +63,18 @@ typeset -i 2 SWISH=1
        typeset -i 2 QRENCODE=1
        
        # Default mount options
       -MOUNTOPTS="rw,noatime,nodev"
       -
       -# prefix for temporary files
       -TMPPREFIX="/dev/shm/$RANDOM.$RANDOM."
       +typeset      MOUNTOPTS="rw,noatime,nodev"
        
        # Makes glob matching case insensitive
        unsetopt CASE_MATCH
        
       -typeset -AH  OPTS             # Command line options (see main())
       +typeset -AH OPTS              # Command line options (see main())
        
       -# Command context
       -typeset -H _UID               # Running user identifier
       -typeset -H _GID               # Running user group identifier
       -typeset -H _TTY               # Connected input terminal
       +# Command context (see _whoami())
       +typeset -H  _USER             # Running username
       +typeset -Hi _UID              # Running user identifier
       +typeset -Hi _GID              # Running user group identifier
       +typeset -H  _TTY              # Connected input terminal
        
        # Tomb context (see _plot())
        typeset -H TOMBPATH           # Full path to the tomb
       t@@ -87,8 +91,8 @@ typeset -H TOMBPASSWORD       # Raw tomb passphrase    (see gen_key(), ask_key_p
        typeset -aH TOMBTMPFILES      # Keep track of temporary files
        typeset -aH TOMBLOOPDEVS      # Keep track of used loop devices
        
       -# Make sure sbin is in PATH
       -PATH+=:/sbin:/usr/sbin
       +# Make sure sbin is in PATH (man zshparam)
       +path+=( /sbin /usr/sbin )
        
        # For gettext
        export TEXTDOMAIN=tomb
       t@@ -116,15 +120,18 @@ _endgame() {
            TOMBSECRET="$rr";    unset TOMBSECRET
            TOMBPASSWORD="$rr";  unset TOMBPASSWORD
        
       +    # Clear temporary files
            for f in $TOMBTMPFILES; do
                ${=WIPE} "$f"
            done
            unset TOMBTMPFILES
        
       +    # Detach loop devices
            for l in $TOMBLOOPDEVS; do
                losetup -d "$l"
            done
            unset TOMBLOOPDEVS
       +
        }
        
        # Trap functions for the _endgame event
       t@@ -138,52 +145,111 @@ TRAPPIPE() { _endgame PIPE  }
        TRAPTERM() { _endgame TERM  }
        TRAPSTOP() { _endgame STOP  }
        
       -check_shm() {
       -    # TODO: configure which tmp dir to use from a cli flag
       -    SHMPREFIX=/dev/shm
       +# Identify the running user
       +# Set global variables _UID, _GID, _TTY, and _USER, either from the
       +# command line, -U, -G, -T, respectively, or from the environment.
       +# Also update USERNAME and HOME to maintain consistency.
       +_whoami() {
       +    # Set global variables
       +    typeset -gi _GID _UID
       +    typeset -g  _TTY _USER
       +
       +    # Get GID from option -G or the environment
       +    option_is_set -G \
       +        && _GID=$(option_value -G) || _GID=$(id -g)
       +
       +    # Get UID from option -U or the environment
       +    option_is_set -U \
       +        && _UID=$(option_value -U) || _UID=$(id -u)
       +
       +    # Set username from UID or environment
       +    [[ -n $SUDO_USER ]]             && _USER=$SUDO_USER
       +    [[ -z $_USER && -n $USERNAME ]] && _USER=$USERNAME
       +    [[ -z $_USER ]]                 && _USER=$(id -u)
       +#    _verbose "Identified caller: ::1 username:: (::2 UID:::::3  GID::)" \
       +#        $_USER $_UID $_GID
       +
       +    # Update USERNAME accordingly if we can
       +    [[ EUID == 0 && $_USER != $USERNAME ]] && {
       +#        _verbose "Updating USERNAME from '::1 USERNAME::' to '::2 _USER::')" \
       +#            $USERNAME $_USER
       +        USERNAME=$_USER
       +    }
       +
       +    # Force HOME to _USER's HOME if necessary
       +    local home=$(awk -F: "/$_USER/ { print \$6 }" /etc/passwd 2>/dev/null)
       +    [[ $home == $HOME ]] || {
       +        _verbose "Updating HOME to match user's: ::1 home:: (was ::2 HOME::)" \
       +            $home $HOME
       +        HOME=$home }
       +
       +    # Get connecting TTY from option -T or the environment
       +    option_is_set -T && _TTY=$(option_value -T)
       +    [[ -z $_TTY ]]   && {
       +        _TTY=$TTY
       +        _verbose "Identified caller from tty ::1 TTY::)" $_TTY }
       +}
        
       -    [[ -k /dev/shm ]] || [[ -k /run/shm ]] && { SHMPREFIX=/run/shm } \
       -        || {
       -        # mount the tmpfs if the SO doesn't already
       -        mkdir /run/shm
       -        (( $? )) && _failure "Fatal error creating a directory for temporary files"
       +# Ensure temporary files remain in RAM
       +# Set global variable TMPPREFIX
       +# TODO: configure which tmp dir to use from a cli flag
       +_ensure_safe_memory check_shm() {
       +    local shmprefix=""
        
       -        mount -t tmpfs tmpfs /run/shm \
       -            -o nosuid,noexec,nodev,mode=0600,uid=$_UID,gid=$_GID
       -        (( $? )) && _failure "Fatal error mounting tmpfs in /run/shm for temporary files"
       +    # Set $shmprefix to something sensible
       +    [[ -z $shmprefix && -k /dev/shm ]] \
       +        && shmprefix=/dev/shm || shmprefix=/run/shm
        
       -        SHMPREFIX=/run/shm
       +    _whoami    # Set _UID, _GID, _TTY, _USER
       +
       +    # Mount the tmpfs if the OS doesn't already
       +    [[ -k $shmprefix ]] || {
       +        mkdir -p $shmprefix/$_UID || {
       +            _failure "Fatal error creating a directory for temporary files" }
       +
       +        mount -t tmpfs tmpfs $shmprefix/$_UID \
       +            -o nosuid,noexec,nodev,mode=0700,uid=$_UID,gid=$_GID
       +        [[ $? == 0 ]] || {
       +            _failure "Cannot mount tmpfs in ::1 shm path::" $shmprefix }
            }
        
       -    # setup a special env var for zsh to create temp files that will
       -    # then be deleted at the exit of each function using them.
       -    TMPPREFIX="$SHMPREFIX/$RANDOM.$RANDOM."
       +    # Ensure all temporary files go into a user-specific directory for
       +    # additional safety
       +    mkdir -m 0700 -p $shmprefix/$_UID || {
       +        _failure "Fatal error creating a directory for temporary files" }
       +
       +    # Set a global environment variable to ensure zsh will use that
       +    # directory in RAM to keep temporary files by setting an.  They
       +    # will be created on demand and deleted as soon as the function
       +    # using them ends.
       +    TMPPREFIX="$shmprefix/$_UID/$RANDOM$RANDOM."
       +
            return 0
        }
        
        # Define sepulture's plot (setup tomb-related arguments)
       -# Synopsis: _plot "/path/to/the.tomb"
       +# Synopsis: _plot /path/to/the.tomb
        _plot() {
            # We set global variables
            typeset -g TOMBPATH TOMBDIR TOMBFILE TOMBNAME
            
            TOMBPATH="$1"
       -    _verbose '_plot TOMBPATH = ::1 tomb path::' $TOMBPATH
       +#    _verbose '_plot TOMBPATH = ::1 tomb path::' $TOMBPATH
        
            TOMBDIR=$(dirname $TOMBPATH)
       -    _verbose '_plot TOMBDIR  = ::1 tomb dir::'  $TOMBDIR
       +#    _verbose '_plot TOMBDIR  = ::1 tomb dir::'  $TOMBDIR
        
            TOMBFILE=$(basename $TOMBPATH)
       -    _verbose '_plot TOMBFILE = ::1 tomb file::' $TOMBFILE
       +#    _verbose '_plot TOMBFILE = ::1 tomb file::' $TOMBFILE
        
            # The tomb name is TOMBFILE without an extension.
            # It can start with dots: ..foo.tomb -> ..foo
            TOMBNAME="${TOMBFILE%\.[^\.]*}"
       -    _verbose '_plot TOMBNAME = ::1 tomb name::' $TOMBNAME
       +#    _verbose '_plot TOMBNAME = ::1 tomb name::' $TOMBNAME
        
            # Normalize TOMBFILE name
            TOMBFILE="${TOMBNAME}.tomb"
       -    _verbose '_plot TOMBFILE = ::1 tomb file:: (normalized)' $TOMBFILE
       +#    _verbose '_plot TOMBFILE = ::1 tomb file:: (normalized)' $TOMBFILE
        
            # Normalize TOMBPATH
            TOMBPATH="${TOMBDIR}/${TOMBFILE}"
       t@@ -415,8 +481,9 @@ lo_preserve() {
        
        # eventually used for debugging
        dump_secrets() {
       -    _verbose "TOMBFILE: ::1 tomb file::" $TOMBPATH
       -    _verbose "TOMBFILE: ::1 tomb file::" $TOMBFILE
       +    _verbose "TOMBPATH: ::1 tomb path::" $TOMBPATH
       +    _verbose "TOMBNAME: ::1 tomb name::" $TOMBNAME
       +
            _verbose "TOMBKEY: ::1 key:: chars long" ${#TOMBKEY}
            _verbose "TOMBKEYFILE: ::1 key file::" $TOMBKEYFILE
            _verbose "TOMBSECRET: ::1 secret:: chars long" ${#TOMBSECRET}
       t@@ -611,33 +678,47 @@ progress() {
        
        }
        
       -# Check what's installed
       -check_bin() {
       -    # check for required programs
       +# Check program dependencies
       +#
       +# Tomb depends on system utilities that must be present, and other
       +# functionality that can be provided by various programs according to
       +# what's available on the system.  If some required commands are
       +# missing, bail out.
       +_ensure_dependencies check_bin() {
       +
       +    # The messages system requires gettext
       +    command -v gettext >& - || {
       +        echo "Missing required dependency: gettext.  Please install it."
       +        exit 1
       +    }
       +
       +    # Check for required programs
            for req in cryptsetup pinentry sudo gpg; do
       -        command -v $req >& - || exitv=1 _failure "Cannot find ::1::. It's a requirement to use Tomb, please install it." $req
       +        command -v $req >& - || {
       +            _failure "Missing required dependency ::1 command::.  Please install it." $req }
            done
        
       -    export PATH=/sbin:/usr/sbin:$PATH
       +    # Ensure system binaries are available in the PATH
       +    path+=(/sbin /usr/sbin) # zsh magic
        
       -    # which dd command to use
       +    # Which dd command to use
            command -v dcfldd >& - && DD=(dcfldd statusinterval=1)
        
       -    # which wipe command to use
       +    # Which wipe command to use
            command -v wipe >& - && WIPE=(wipe -f -s)
        
       -    # check for filesystem creation progs
       +    # Check for filesystem creation programs
            command -v mkfs.ext4 >& - && MKFS=(mkfs.ext4 -q -F -j -L)
        
       -    # check for steghide
       +    # Check for steghide
            command -v steghide >& - || STEGHIDE=0
       -    # check for resize
       +    # Check for resize
            command -v e2fsck resize2fs >& - || RESIZER=0
       -    # check for KDF auxiliary tools
       +    # Check for KDF auxiliary tools
            command -v tomb-kdb-pbkdf2 >& - || KDF=0
       -    # check for Swish-E file content indexer
       +    # Check for Swish-E file content indexer
            command -v swish-e >& - || SWISH=0
       -    # check for QREncode for paper backups of keys
       +    # Check for QREncode for paper backups of keys
            command -v qrencode >& - || QRENCODE=0
        }
        
       t@@ -1701,65 +1782,87 @@ mount_tomb() {
            return 0
        }
        
       -# ## Hooks execution
       +## HOOKS EXECUTION
       +#
       +# Execution of code inside a tomb may present a security risk, e.g.,
       +# if the tomb is shared or compromised, an attacker could embed
       +# malicious code.  When in doubt, open the tomb with the -n switch in
       +# order to skip this feature and verify the files mount-hooks and
       +# bind-hooks inside the tomb yourself before letting them run.
       +
       +# Mount files and directories from the tomb to the current user's HOME.
       +#
       +# Synopsis: exec_safe_bind_hooks /path/to/mounted/tomb
       +#
       +# This can be a security risk if you share tombs with untrusted people.
       +# In that case, use the -n switch to turn off this feature.
        exec_safe_bind_hooks() {
       -    if [[ -n ${(k)OPTS[-o]} ]]; then
       -        MOUNTOPTS=${OPTS[-o]}
       -    fi
       -    local MOUNTPOINT="${1}"
       -    local ME=${SUDO_USER:-$(whoami)}
       -    local HOME=$(awk -v a="$ME" -F ':' '{if ($1 == a) print $6}' /etc/passwd 2>/dev/null)
       -    if [ $? -ne 0 ]; then
       -        _warning "How pitiful!  A tomb, and no HOME."
       -        return 1
       -    fi
       -    if [ -z "$MOUNTPOINT" -o ! -d "$MOUNTPOINT" ]; then
       -        _warning "Cannot exec bind hooks without a mounted tomb."
       -        return 1
       -    fi
       -    if ! [ -r "$MOUNTPOINT/bind-hooks" ]; then
       -        _verbose "bind-hooks not found in ::1 mount point::" $MOUNTPOINT
       -        return 1
       -    fi
       -    typeset -al mounted
       -    typeset -Al maps
       -    maps=($(<"$MOUNTPOINT/bind-hooks"))
       +    local mnt="$1"   # First argument is the mount point of the tomb
       +
       +    # Default mount options are overridden with the -o switch
       +    [[ -n ${(k)OPTS[-o]} ]] && MOUNTOPTS=${OPTS[-o]}
       +
       +    # No HOME set? Note: this should never happen again.
       +    [[ -z $HOME ]] && {
       +        _warning "How pitiful!  A tomb, and no HOME."
       +        return 1 }
       +
       +    [[ -z $mnt || ! -d $mnt ]] && {
       +        _warning "Cannot exec bind hooks without a mounted tomb." 
       +        return 1 }
       +
       +    [[ ! -r "$mnt/bind-hooks" ]] && {
       +        _verbose "bind-hooks not found in ::1 mount point::" $mnt
       +        return 1 }
       +
       +    typeset -Al maps        # Maps of files and directories to mount
       +    typeset -al mounted     # Track already mounted files and directories
       +
       +    maps=($(<"$mnt/bind-hooks"))
            for dir in ${(k)maps}; do
       -        if [ "${dir[1]}" = "/" -o "${dir[1,2]}" = ".." ]; then
       +        [[ "${dir[1]}" == "/" || "${dir[1,2]}" == ".." ]] && {
                    _warning "bind-hooks map format: local/to/tomb local/to/\$HOME"
       -            continue
       -        fi
       -        if [ "${${maps[$dir]}[1]}" = "/" -o "${${maps[$dir]}[1,2]}" = ".." ]; then
       +            continue }
       +
       +        [[ "${${maps[$dir]}[1]}" == "/" || "${${maps[$dir]}[1,2]}" == ".." ]] && {
                    _warning "bind-hooks map format: local/to/tomb local/to/\$HOME.  Rolling back"
                    for dir in ${mounted}; do umount $dir; done
       -            return 1
       -        fi
       +            return 1 }
       +
                if [ ! -r "$HOME/${maps[$dir]}" ]; then
                    _warning "bind-hook target not existent, skipping ::1 home::/::2 subdir::" $HOME ${maps[$dir]}
       -        elif [ ! -r "$MOUNTPOINT/$dir" ]; then
       -            _warning "bind-hook source not found in tomb, skipping ::1 mount point::/::2 subdir::" $MOUNTPOINT $dir
       +        elif [ ! -r "$mnt/$dir" ]; then
       +            _warning "bind-hook source not found in tomb, skipping ::1 mount point::/::2 subdir::" $mnt $dir
                else
       -            mount -o bind,$MOUNTOPTS $MOUNTPOINT/$dir $HOME/${maps[$dir]}
       +            mount -o bind,$MOUNTOPTS $mnt/$dir $HOME/${maps[$dir]}
                    mounted+=("$HOME/${maps[$dir]}")
                fi
            done
        }
        
       -# Post mount hooks
       +# Execute automated actions configured in the tomb.
       +#
       +# Synopsis: exec_safe_post_hooks /path/to/mounted/tomb [open|close]
       +#
       +# If an executable file named 'post-hooks' is found inside the tomb,
       +# run 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.
       +# If you're mounting an untrusted tomb, be safe and use the -n switch
       +# to verify what it would run if you let it.  This feature opens the
       +# possibility to make encrypted executables.
        exec_safe_post_hooks() {
       -    local mnt=$1 # first argument is where the tomb is mounted
       -    local ME=${SUDO_USER:-$(whoami)}
       -    if ! [ -x ${mnt}/post-hooks ]; then return; fi
       -    # if 'post-hooks' is found inside the tomb, check it: if it is an
       -    # executable, launch it as a user this might need a dialog for
       -    # security on what is being run, however we expect you know well
       -    # what is inside your tomb.  this feature opens the possibility to
       -    # make encrypted executables.
       -    cat ${mnt}/post-hooks | head -n1 | grep '^#!/'
       -    if [ $? = 0 ]; then
       -        _success "Post hooks found, executing as user ::1 user name::." $SUDO_USER
       -        exec_as_user ${mnt}/post-hooks "$2" "$1"
       -    fi
       +    local mnt=$1     # First argument is where the tomb is mounted
       +    local act=$2     # Either 'open' or 'close'
       +
       +    # Only run if post-hooks has the executable bit set
       +    [[ -x $mnt/post-hooks ]] || return
       +
       +    # If the file starts with a shebang, run it.
       +    cat $mnt/post-hooks | head -n1 | grep '^#!\s*/' &> /dev/null
       +    [[ $? == 0 ]] && {
       +        _success "Post hooks found, executing as user ::1 user name::." $USERNAME
       +        exec_as_user $mnt/post-hooks $act $mnt
       +    }
        }
        
        # }}} - Tomb open
       t@@ -2322,8 +2425,12 @@ slam_tomb() {
        # }}} - Tomb close
        
        # {{{ Main routine
       +
        main() {
        
       +    _ensure_dependencies  # Check dependencies are present or bail out
       +    _ensure_safe_memory   # Check available memory can be used safely
       +
            local -A subcommands_opts
            ### Options configuration
            #
       t@@ -2457,11 +2564,9 @@ main() {
                done
            fi
        
       -    # when we run as root, we remember the original uid:gid
       -    # to set permissions for the calling user and drop privileges
       -    if option_is_set -U; then _UID="`option_value -U`"; fi
       -    if option_is_set -G; then _GID="`option_value -G`"; fi
       -    if option_is_set -T; then _TTY="`option_value -T`"; fi
       +    # When we run as root, we remember the original uid:gid to set
       +    # permissions for the calling user and drop privileges
       +    _whoami # Reset _UID, _GID, _TTY
        
            [[ "$PARAM" == "" ]] && {
                _verbose "Tomb command: ::1 subcommand::" $subcommand
       t@@ -2469,8 +2574,9 @@ main() {
                _verbose "Tomb command: ::1 subcommand:: ::2 param::" $subcommand $PARAM
            }
        
       -    [[ $_UID == "" ]] || {
       -        _verbose "Caller: uid[::1 uid::], gid[::2 gid::], tty[::3 tty::]." $_UID $_GID $_TTY
       +    [[ -z $_UID ]] || {
       +        _verbose "Caller: uid[::1 uid::], gid[::2 gid::], tty[::3 tty::]." \
       +            $_UID $_GID $_TTY
            }
        
            case "$subcommand" in
       t@@ -2590,15 +2696,9 @@ EOF
        # }}}
        
        # {{{ Run
       -check_bin
        
       -check_shm
       +main $@ || exit $?   # Prevent `tomb source tomb` from exiting
        
       -main $@
       -ret=$?
       -if [[ $ret != 0 ]]; then #this "if" seems useless, but avoid source tomb source from exiting
       -    exit $ret
       -fi
        # }}}
        
        # -*- tab-width: 4; indent-tabs-mode:nil; -*-