|  Updated tripl scriptForum: Programming and Scripting
 Topic: Updated tripl script
 started by: WDef
 
  Posted by WDef on Aug. 06 2008,21:39 For your orgastic overstimulation, here is the updated  version of the tripl script referred to < here  > , since it doesn't have a home as yet. 
 If you tried the earlier version (in the loopaes extension), note the cli switches have changed. Type tripl -h
 
 It tests for correct gnupg and losetup etc which will slow it down on very old machines; if anyone asks I will add a switch to disable these checks.
 
 
 | Code Sample |  | #!/bin/bash
 
 # $Id: tripl,v0.56 4/23/2008 01:55:01 wdef Exp $
 
 # tripl - single or multiple encryption with loop-aes-ciphers
 # (c) 2006-2008 wdef v0.54 <email suppressed for forum post>
 
 # Wrapper to manage single or multiple encrypted partitions using loop-aes v3.x (multikey).
 # Automates gpg key generation, allocation and setup/pulldown of loops, layered embedding of keys,
 # filesystem checking etc. Does not write to /etc/fstab. Note: multiple encryption can use a lot of
 # cpu at times. Single or double encryption with a good password is sufficient (really!).
 # Script skips loops that are already in use and only pulls down encrypted loops chained to device
 # on umounting. Blowfish is not supported since it does not work with multiline keychains in loopaes
 # and is not recommended for large amounts of data.
 # Tries hard to prevent loop collisions if you are also using some loops for other things.
 
 # This is free software. No warranty. Use AYOR.
 
 #===========================================================
 
 ###  Changelog v.3
 # Set example .config in ${HOME} on first run
 # - modules settings and MAXLOOPS removed from config, now automatic.
 # - improved loopfree function
 # - explicit error messages on failed umount or loop pulldown, fixed exit values from pull_down.
 # - added option for manually setting paths to utils.
 # - should work on systems without /sbin in PATH.
 # ## v0.4
 # Added test function for increased iterations with gpg
 # - Now only pulls down loops chained to device (function dechain_loops)
 # ## v0.5
 # Switches changed to use getopts: -f -muknrh
 # Try very hard to prevent loop collisions:
 # - fixed issue with wrong PREVIOUSLOOP when more than one encrypted device mounted.
 # - fixed dechain_loops
 # - loopfree now looks in any field of losetup output for loop numbers
 # Checks that device is there..
 # Allow 3 tries to get password right
 # Clearer messages.
 # VERBOSE setting.
 # -f option allows pointing at alternate config file.
 # ## v0.54
 # Fixed loop pulldown after making new partition.
 # Checks device free before allowing new setup
 # Other stuff
 # ## v0.55
 # Gentle option for forced umount
 # ## v0.56
 # Added ext3
 
 # To Do:
 # add signal trap to pulldown loop if device overwrite interrupted
 
 
 #===================// FUNCTIONS //============================
 
 
 help(){
 cat <<"EOF"
 tripl - single or multiple encryption with loop-aes
 (c) 2006-2008 wdef v0.56
 Usage: tripl [-f <file> ] [-muknrh ]
 Option:
 -m = mount
 -u = unmount
 -k = make key
 -n = set up encrypted partition (destroys data!)
 -r = check, repair encrypted filesystem
 -f = use <file> as triplrc instead of ~/.triplrc
 -h = this
 Set partition name, keys, order of ciphers, mountpoint and MODE
 (1,2,3 for single,double,triple encryption) in triplrc
 EOF
 }
 
 
 set_example_config(){
 cat <<EOF
 
 # Make these settings in $HOME/.triplrc, where $HOME is root's home dir.
 
 DEVICE=/dev/your_device_here
 MOUNTPT=/mnt/your_mountpoint_here
 
 #MODE=3		# Triple encryption
 MODE=2		# Double encryption
 #MODE=1		# Single encryption
 
 # You can rearrange the order of ciphers, but only for a new setup:
 #- you'll have to destroy data by runnning tripl -n again if you change this (so back up data first).
 
 CIPHER[1]=serpent128
 CIPHER[2]=twofish128
 CIPHER[3]=AES128
 
 # Run tripl -k to make a key, then set it here:
 
 key[1]=/path_to_your_key1
 key[2]=/path_to_your_key2
 key[3]=/path_to_your_key3
 
 FS=ext2		# Filesystem: ext2, ext3
 #FS=ext3
 
 
 #EMBED=no
 EMBED=yes	# If enabled, the user only need specify one external gpg-encrypted key (key[1]) in user settings.
 # Other keys will be automatically generated for each encryption layer by tripl -n (prompts user
 # for passwords) and successively embedded in the encryption layer previous to that being set up.
 # This also means an attacker has to crack the first encryption layer just to get the
 # encrypted key for the second, and so on. No effect if MODE=1
 
 # If disabled, a seperate external (detached) key should be created with tripl -k for each
 # encryption layer and set in this config file prior to running tripl -n
 
 #FORCE=OFF	# No attempt to interrupt processes to do umount
 FORCE=GENTLE	# Flush buffers and signal politely before resorting to sigkill
 #FORCE=BRUTE	# Only to enable an emergency rapid umount.
 
 VERBOSE=yes	# Show what is being done.
 
 GPGHOME="${HOME}/.gnupg"
 
 DISABLE_NEW=yes	# Disable new encrypted paritition switch as a double failsafe measure to prevent partition destruction
 # Enable when you're sure you need it.
 
 
 # If non-standard /path/to/utils, set here:
 # losetup=
 # mount=
 # umount=
 # fsck=
 # modprobe=
 # fdisk=
 EOF
 }
 
 
 check_losetup(){
 if $(which strings &>/dev/null); then
 strings $losetup | grep -q -s multi-key-v3
 return $?
 elif grep --version | grep -q 'GNU grep' 2>/dev/null; then
 grep -q -a -s multi-key-v3 $losetup
 return $?
 else
 echo "Error: can't check losetup compatibility"
 echo "Install strings or GNU grep before proceeding."
 exit 1
 fi
 }
 
 
 run_checks(){
 if [ $EUID -ne 0 ]; then echo "You're not root."; exit 1; fi
 if [ $# -eq 0 ]; then help; exit 1; fi
 if ! which gpg &>/dev/null; then echo "Can't find GnuPG"; exit 1; fi
 if [ $MAXLOOPS -eq 0 ]; then echo "Can't find any loop devices"; exit 1;fi
 }
 
 
 set_modules(){
 # Set modules to match ciphers
 for p in $(seq 1 $MODE); do
 case ${CIPHER[p]} in
 aes128|AES128) MODULE[$p]=loop;;
 serpent128|SERPENT128) MODULE[$p]=loop_serpent;;
 twofish128|TWOFISH128) MODULE[$p]=loop_twofish;;
 *) echo "Error: unsupported cipher ${CIPHER[p]}"; exit 1;;
 esac
 done
 }
 
 check_device_free(){
 if $losetup -a | grep -wq ${DEVICE} || grep -wq ${DEVICE} /proc/mounts; then
 echo "Error: ${DEVICE} is in use."
 exit 1
 fi
 }
 
 
 conf_checks(){
 
 if [ "$CLI_CONFIG" != yes ]; then
 if [ -f "${HOME}/.triplrc" ]; then
 . ${HOME}/.triplrc
 else
 set_example_config >${HOME}/.triplrc
 echo "Edit ${HOME}/.triplrc for your desired setup"
 echo "Then run tripl again."
 exit 0
 fi
 fi
 
 # Default utils locations if not set or in  PATH
 losetup=${losetup:=/sbin/losetup}
 mount=${mount:=/bin/mount}
 umount=${umount:=/bin/umount}
 fsck=${fsck:=/sbin/fsck}
 modprobe=${modprobe:=/sbin/modprobe}
 fdisk=${fdisk:=/sbin/fdisk}
 
 # Do some basic checks
 if [ -z "${DEVICE}" ]; then "DEVICE is unset"; exit 1; fi
 if [ -z "${MOUNTPT}" ]; then "MOUNTPT is unset"; exit 1; fi
 if [ ! -d "${MOUNTPT}" ]; then echo "${MOUNTPT} does not exist."; exit 1;fi
 if [ ! -b "${DEVICE}" ]; then echo "$DEVICE is not a valid block device"; exit 1; fi
 if ! $fdisk -l | awk '/^\/dev/{print $1}'| grep -wq ${DEVICE}; then
 echo "Can't find device ${DEVICE}.  Is it connected?"
 exit 1
 fi
 case $FS in
 ext2|ext3);;
 *) echo "Invalid filesystem setting \"$FS\""; exit 1;;
 esac
 
 if ! check_losetup; then
 echo "Error: your $losetup is incompatible with loop-aes v3.x"
 echo "See the loop-aes README for details."
 exit 1
 fi
 
 set_modules
 }
 
 
 
 loopfree(){
 
 # Could(?) replace this whole fn with:
 # perl -e 'map($t[$_]=1, map(/loop(\d)/,`losetup -a`)); $i=0; $i++ until !$t[$i]; ($i==8 and exit 1) || print $i'
 
 # Safest - just get all the used loop numbers, regardless of what field they appear in
 # This should prevent evil from somehow managing something silly like absent loop on one
 # loop of chain, leaving next loop in chain orphaned, and loopfree would then report the first (busy)
 # loop as free =>  overwrite encrypted loop (it  happened).
 
 A=$($losetup -a | perl -lane 'while (/\/dev\/loop(\d)/g) { print $1 }' | sort | uniq)
 
 if [ -z "$A" ]; then
 # Loop 0 is free
 NEXTLP=0
 return 0
 fi
 Y=0
 for i in $A; do
 if [ $i -eq $Y ]; then
 (( Y = i + 1 ))
 continue
 else
 # Loop $Y is free
 NEXTLP=$Y
 return 0
 fi
 done
 if [ $Y -eq $MAXLOOPS ]; then
 # No free loops
 return 1
 else
 # loop $Y is free
 NEXTLP=$Y
 return 0
 fi
 }
 
 
 dechain_loops(){
 # Outputs encrypted loop devices chained to device $1
 export A=$1
 NUMLOOPS=0
 until [ -z "${A}" ]; do
 LOOP=$($losetup -a | awk -F: '$3 ~ ENVIRON["A"] {print $1}')
 if [ -n "${LOOP}" ]; then
 echo "${LOOP}"  # important: output includes newline
 (( NUMLOOPS++ ))
 fi
 export A=${LOOP}
 done
 }
 
 
 pull_down(){
 X=$1
 if grep -wq "${MOUNTPT}" /proc/mounts; then
 case $FORCE in
 
 BRUTE)  # just kill 'em.  Could damage filesystem and/or lose some data
 fuser -m -k ${MOUNTPT};;
 
 GENTLE) # Be gentle with me
 s[0]=-SIGHUP; s[1]=-SIGTERM; s[2]=-SIGKILL
 sync; sync
 q=0
 for SIG in ${s[*]}; do
 fuser -m -k $SIG ${MOUNTPT}
 sleep 1
 if fuser -ms ${MOUNTPT}; then
 echo -n "$SIG failed, "
 if [ $q -eq 2 ]; then
 echo "Error: processes still running on ${MOUNTPT}"
 exit
 fi
 echo "trying ${s[q+1]} .."
 else
 break
 fi
 (( q = q + 1 ))
 done;;
 OFF);;
 
 *) echo "Error: unrecognized FORCE configuration \"$FORCE\"";;
 
 esac
 
 $umount ${MOUNTPT}
 grep -wq "${MOUNTPT}" /proc/mounts && echo "Error: umount failed!"
 else
 [ "$VERBOSE" = yes ] && echo "${MOUNTPT} is not in use"
 fi
 
 # Pull down our encrypted loops chained on top of device
 
 dechain_loops ${DEVICE} | sort -r | while read D; do
 [ "$VERBOSE" = yes ] && echo "Pulling down ${D} .."
 $losetup -d $D
 X=$?
 if [ $X -ne 0 ]; then
 echo "Error: free up ${D} and try again."
 exit $X
 fi
 done && exit $X
 }
 
 
 
 ask_user(){
 while true; do
 echo -n "$1? (y/N) "
 read
 case $REPLY in
 y|Y|y*|Y*) if [ "$1" = Abort ]; then exit 1; fi;;
 n|N|n*|N*) if [ "$1" = Abort ]; then break; fi;;
 *) echo "Invalid response";;
 esac
 done
 }
 
 
 
 check_gpg_iterations(){
 echo
 echo x | gpg --no-tty --passphrase-fd 3 3< <(echo whatever) --symmetric >${gpgtest}
 TESTSTR=$(gpg --no-tty --passphrase-fd 3 3< <(echo whatever)  --decrypt -v -v <${gpgtest} 2>&1 | grep -E "salt.+count")
 ITERFLAG=${TESTSTR##*[ ]}
 rm -f ${gpgtest}
 case $ITERFLAG in
 # newer gnupg versions put () around iteration flag
 208|\(208\)) echo "Good, GnuPG is using increased iterations flag = $ITERFLAG";;
 96|\(96\)) echo "GnuPG is using only standard iterations flag = $ITERFLAG"
 echo "See loop-aes README for details."
 ask_user Abort;;
 *) echo "WTF?? unknown gpg iteration counts flag $ITERFLAG"; exit 1;;
 esac
 }
 
 
 makekey(){
 check_gpg_iterations &>/dev/tty
 head -c 2925 /dev/random | uuencode -m - | head -n 66 | tail -n 65 \
 | gpg --symmetric -a
 return
 }
 
 
 checkloops(){
 NUMBUSY=$($losetup -a|wc -l)
 (( NUMFREELOOPS = MAXLOOPS - NUMBUSY ))
 if [ $NUMFREELOOPS -lt $MODE ]; then
 echo "Insufficient loops free ($NUMFREELOOPS)"
 echo "Require $MODE free loop device(s)."
 exit 1
 fi
 }
 
 
 overwrite_partition(){
 checkloops
 loopfree || pull_down 1
 LP=/dev/loop$NEXTLP
 head -c 15 /dev/urandom | uuencode -m - | head -n 2 | tail -n 1 | losetup -p 0 -e AES128 $LP ${DEVICE}
 dd if=/dev/zero of=$LP bs=4k conv=notrunc 2>/dev/null
 losetup -d $LP
 }
 
 
 setup_loops(){
 [ "${1}" != new ] && check_device_free # for new setup, this has already been checked
 checkloops
 echo
 if [ $EMBED = yes ]; then
 if [ ! -e "${key[1]}" ]; then
 echo "Error: Can't find key ${key[1]} - need one external key for embedded mode."
 exit 1
 fi
 if [ "${1}" = new ]; then
 [ "$VERBOSE" = yes ] && echo ">>>>>> Using key ${key[1]} for loop1 <<<<<<"
 else
 echo "Enter $MODE password(s):"
 fi
 else
 echo "Enter $MODE password(s):"
 fi
 
 
 for k in $(seq 1 $MODE); do
 
 $modprobe ${MODULE[k]} || pull_down 1
 loopfree || pull_down 1
 TOPLOOP="/dev/loop$NEXTLP"
 
 if [ $k -gt 1 ]; then
 # Sanity check:
 PL=$(dechain_loops ${DEVICE}| tail -n 1)
 if [ ${PREVIOUSLOOP} != ${PL} ]; then
 echo "[$k] Error! dechain_loops says previous loop is ${PL}"
 echo "But here previousloop = ${PREVIOUSLOOP}"
 exit 1
 fi
 fi
 
 if [ $EMBED = yes ]; then
 if [ $k -eq 1 ]; then
 # No offset key on first loop
 [ "$VERBOSE" = yes ] && echo "[$k] Setting up ${TOPLOOP} on ${DEVICE} .."
 p=0
 while ! $losetup -e ${CIPHER[1]} -K ${key[1]} -G ${GPGHOME} ${TOPLOOP} ${DEVICE}; do
 (( p++ ))
 if [ $p -eq 3 ]; then echo "3rd failed attempt, exiting .."; pull_down 1; fi
 echo "Try again ($p) .."
 done
 else
 if [ "${1}" = new ]; then
 echo
 echo ">>>>>> [$k] Making embedded key on ${PREVIOUSLOOP}  <<<<<<"
 echo "Enter new password at three prompts .."
 yes "" | dd of=${PREVIOUSLOOP} bs=512 count=16
 makekey | dd of=${PREVIOUSLOOP} conv=notrunc
 fi
 [ "$VERBOSE" = yes ] && echo "[$k] Setting up ${TOPLOOP} on ${PREVIOUSLOOP} .."
 p=0
 while ! $losetup -e ${CIPHER[k]} -K ${PREVIOUSLOOP} -o 8192 -G ${GPGHOME} ${TOPLOOP} ${PREVIOUSLOOP}; do
 (( p++ ))
 if [ $p -eq 3 ]; then echo "3rd failed attempt, exiting .."; pull_down 1; fi
 echo "Try again ($p) .."
 done
 
 fi
 else
 if [ ! -e "${key[k]}" ]; then
 echo "[$k] Error: Can't find key ${key[k]}"
 pull_down 1
 fi
 [ $k -eq 1 ] && PREVIOUSLOOP=${DEVICE}
 [ "$VERBOSE" = yes ] && echo "[$k] Setting up ${TOPLOOP} on ${PREVIOUSLOOP} .."
 p=0
 while ! $losetup -e ${CIPHER[k]} -K ${key[k]} -G ${GPGHOME} ${TOPLOOP} ${PREVIOUSLOOP}; do
 (( p++ ))
 if [ $p -eq 3 ]; then echo "3rd failed attempt, exiting .."; pull_down 1; fi
 echo "Try again ($p) .."
 done
 fi
 PREVIOUSLOOP=${TOPLOOP}
 done
 
 }
 
 #=====================// MAIN //==================================
 
 
 gpgtest=/tmp/gpgtest.$RANDOM.$$
 
 MAXLOOPS=$(ls /dev/loop* | wc -l)
 
 run_checks $*
 
 # Parse cli options
 
 ARGS="$@"
 
 # Catch malformed options not having 2 chars
 for P in $ARGS; do
 case $P in
 -*) if [ ${#P} -ne 2 ]; then
 echo "Unknown option $P. Try tripl -h"; exit 1
 fi;;
 esac
 done
 
 if echo $ARGS | grep -q 'f'; then
 case $ARGS in
 -f*):;;
 *f*) echo "Error: -f option must be first"
 exit 1;;
 esac
 fi
 
 CLI_CONFIG=""
 
 
 while getopts ":f:nmukrh" Option; do
 
 case $Option in
 
 f) . ${OPTARG} || exit 1
 [ "$VERBOSE" = yes ] && echo "Using config file ${OPTARG} .."
 CLI_CONFIG=yes;;
 
 n)
 conf_checks
 if [ $DISABLE_NEW = yes ]; then
 echo "New partition switch disabled in user settings"
 exit 0
 fi
 check_device_free
 echo
 echo "WARNING: this will destroy all data on $DEVICE !"
 echo "~~~~~~~"
 
 while true; do
 echo -n "Last chance to exit. Are you sure you want to proceed? (YeS/n) "
 read
 case $REPLY in
 YeS) break;;
 n|N|n*|N*) exit 0;;
 y|y*|Y|Yes|YE*) echo "You must type YeS to proceed.";;
 *) echo "Invalid response.";;
 esac
 done
 
 
 echo "Preparing device ${DEVICE} .. pls wait .."
 overwrite_partition
 
 setup_loops new
 echo
 echo ">>>>>> Making filesystem on ${TOPLOOP} <<<<<<"
 mkfs -t $FS ${TOPLOOP}
 pull_down 0;;
 
 
 m)
 conf_checks
 if grep -wq "${MOUNTPT}" /proc/mounts; then echo "${MOUNTPT} in use."; exit 1; fi
 setup_loops
 $mount -t $FS ${TOPLOOP} ${MOUNTPT}
 if grep -wq "${TOPLOOP}" /proc/mounts; then echo "OK"; else pull_down 1; fi;;
 
 u)
 conf_checks
 pull_down 0;;
 
 k)
 conf_checks
 while true; do
 echo -n "Enter path and filename of new key (CNTRL-C = quit): "
 read
 DIR=$(dirname ${REPLY})
 if [ -e "${REPLY}" ]; then
 echo "File $REPLY already exists."; continue
 fi
 if [ ! -d "${DIR}" ]; then
 echo "Invalid path ${DIR}"; continue
 fi
 if [ ! -w "${DIR}" ]; then
 echo "${DIR} not writeable"; continue
 fi
 break
 done
 echo "Making key ${REPLY} .."
 makekey >${REPLY}
 if [ $? -eq 0 ]; then
 echo "Done."
 echo "Set your new key location at top of the script."
 else
 rm -f ${REPLY} # gpg will provide error messages if it fails.
 exit 1
 fi;;
 
 
 r)
 conf_checks
 if grep -wq "${MOUNTPT}" /proc/mounts; then echo "${MOUNTPT} in use."; exit 1; fi
 echo "Repairing encrypted filesystem on ${DEVICE} .."
 setup_loops
 $fsck -t $FS -C -f -y ${TOPLOOP}
 echo "Done."
 pull_down 0;;
 
 
 h) help; exit 0;;
 
 *)
 A=( echo "$@" )
 BADOPT=${A[OPTIND-1]}
 echo "Unknown option $BADOPT. Try tripl -h"; exit 1;;
 
 esac
 done
 
 shift $(($OPTIND - 1))
 
 | 
 
 |