#! /bin/bash
#
# This is a boot script for SuSE Linux and it probably has to be 
# adapted to other distributions. Look for "# SuSE specific [...]" lines 
# for "rc_failed", "rc_status" and "rc_exit". The former can be deleted 
# (they set and show the status), rc_exit has to be replaced by exit.
#
# CHANGELOG
# Version 1.2
#	- Line length limited
#	- The volume given in fstab need not be a block device but can 
#		be a regular file, too.
#	- Deleted unsupported arguments (like reload)
#	- mount -f only on success of mount
#	- suppress mount output
# Version 1.1, 19.04.2005, Hauke Laging
#	- Bugfix: chmod auf /tmp (rwxrwxrwt)
# Version 1.0.1, 23.02.2005, Hauke Laging
# Version 1.0, 15.01.2005, Hauke Laging, http://www.hauke-laging.de/ 
#	software@hauke-laging.de
#
#    This program is free software; you can redistribute it and/or modify 
#    it under the terms of the GNU General Public License as published by 
#    the Free Software Foundation; either version 2 of the License, or 
#    (at your option) any later version. 
# 
### BEGIN INIT INFO
# Provides:          boot.mount_tmp
# Required-Start:    boot.localfs
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start:     B
# Default-Stop:      
# Short-Description: Mount /tmp on randomly encrypted volume
# Description:       Look for a /tmp entry with loop and noauto options 
#	in /etc/fstab, set up the loop device with an random
#	passphrase, create the file system specified in 
#	/etc/fstab on the loop device and mount it to /tmp.
#	This makes sure that any information in /tmp is securely
#	lost after rebooting. This closes a kind of security gap for 
#	users who encrypt their data volumes and the swap file.
### END INIT INFO
# 

# Check for missing binaries (stale symlinks should not happen)
# Note: Special treatment of stop for LSB conformance
# debug
for prog in awk losetup grep mount
	do
	type $prog &>/dev/null || { echo "'$prog' not installed"; 
	if [ "$1" = "stop" ]; then exit 0;
	else exit 5; fi; }
done

mount_point="/tmp"
shopt -s extglob

# SuSE specific - BEGIN
. /etc/rc.status
# Reset status of this service
rc_reset
# SuSE specific - END

case "$1" in
    start)
	echo -n "Mounting ${mount_point} on a randomly encrypted volume "
	mount_point_tmp="${mount_point//\//}"
	test 0 -lt ${#mount_point_tmp} && rm -rf "${mount_point}/"*
	
	# Check for fstab entry
	#	- must be a device (not e.g. tmpfs)
	#	- mount point must match exactly (^$)
	#	- loop device must be given in mount options (must match /dev/loop[0-9]*)
	#	- encryption must be given in mount options
	
	# check if mount point is already in use
	awk '$2 == "'"${mount_point}"'" {found=1};END{if(found==1)exit(0);else exit(1)}' /proc/mounts &&
		{ echo "Mount point '$mount_point' is already in use (according to /proc/mounts); aborting";
		rc_failed;rc_exit; }

	
	# get and check the (volume) device
	device="$(awk '$1 ~ "^/dev/" && $2 ~ "^'"${mount_point}"'/?$" && $4 ~ "loop=/dev/loop" && $4 ~ '\
'"encryption=" {print NR "_" $1;matched++};END{if(matched==0) exit(1); else if(matched==1) '\
'exit(0);else exit(2)}' /etc/fstab)";rc_awk=$?
	fstab_line="${device%%_*}"
	device="${device#*_}"
	test $rc_awk -eq 1 && { rc_status -s;rc_exit; }
	test $rc_awk -gt 1 && { echo "Several matches for '${mount_point}' in /etc/fstab; aborting.";
		rc_failed;rc_exit; }
	test -b "$device" || test -f "$device" || 
		{ echo "'$device' is neither a valid block device nor a regular file; aborting";
		rc_failed;rc_exit; }
	
	# get and check the loop device
	loop_device="$(awk 'NR == '"${fstab_line}"' {pos=match($4,"(,|^)loop=/dev/loop[0-9]*(,|$)");'\
'if(pos==0) exit(1); else {loop=substr($4,RSTART+5,RLENGTH-5);gsub(",","",loop);sub("^[^l]","",loop);'\
'print loop}}' /etc/fstab)";rc_awk=$?
	test $rc_awk -ne 0 && { echo "loop device could not be found in /etc/fstab; aborting.";
		rc_failed;rc_exit; }
	test -b "$loop_device" || { echo "'$loop_device' is not a valid block device; aborting";
		rc_failed;rc_exit; }
	losetup "$loop_device" &>/dev/null;rc_losetup=$?
	test "$rc_losetup" -eq 0 && { echo "'$loop_device' is already in use; aborting";
		rc_failed;rc_exit; }
	test "$rc_losetup" -eq 2 && { echo "'$loop_device' is not a valid loop device; aborting";
		rc_failed;rc_exit; }
	
	# get and check/prepare the encryption
	encryption="$(awk 'NR == '"${fstab_line}"' {pos=match($4,"(,|^)encryption=[^,]*(,|$)");'\
'if(pos==0) exit(1); else {enc=substr($4,RSTART+11,RLENGTH-11);gsub(",","",enc);sub("^[^e]","",enc);print enc}}' \
/etc/fstab)";rc_awk=$?
	test $rc_awk -ne 0 && { echo "encryption type could not be found in /etc/fstab; aborting.";
		rc_failed;rc_exit; }
# SuSE specific - BEGIN
	case "$encryption" in
		twofish*([0-9]))
			crypto_modules=(cryptoloop loop_fish2 twofish)
			for module in "${crypto_modules[@]}"
				do
				modprobe "$module" || { echo "modprobe '${module}' failed"; 
				if [ "$1" = "stop" ]; then exit 0;
				else rc_failed;rc_exit; fi; }
			done
		;;
		*)
			echo "${warn}Encryption type '${encryption}' is not supported."
			echo "Setting up the loop device may fail due to this.${norm}"
		;;
	esac
# SuSE specific - END
	
	# get and check the file system
	filesystem="$(awk 'NR == '"${fstab_line}"' {print $3}' /etc/fstab)";rc_awk=$?
	grep -q "$filesystem" /proc/filesystems || 
		{ echo "'$filesystem' is not currently supported by kernel (according to /proc/filesystems); aborting";
		rc_failed;rc_exit; }
	case "$filesystem" in
		ext[23])
			type mke2fs &>/dev/null || 
				{ echo "The file system builder for '${filesystem}' could not be found; aborting.";
				rc_failed;rc_exit; }
		;;
		reiserfs)
			type mkreiserfs &>/dev/null || 
				{ echo "The file system builder for '${filesystem}' could not be found; aborting.";
				rc_failed;rc_exit; }
		;;
		reiser4)
			type mkfs.reiser4 &>/dev/null || 
				{ echo "The file system builder for '${filesystem}' could not be found; aborting.";
				rc_failed;rc_exit; }
		;;
		*)
			echo "The file system '${filesystem}' is not supported by this script; aborting."
			rc_failed
			rc_exit
		;;
	esac
	
	# get and prepare the mount options
	# strip off options that are not useful
	patterns=(user users auto noauto loop="${loop_device}" encryption="${encryption}" loop)
	patterns_count=${#patterns[*]}
	awk_patterns=
	for((i=0;i<patterns_count;i++))
		do
		if [ 0 -ne "$i" ]
			then
			awk_patterns="${awk_patterns};"
		fi
		awk_patterns="${awk_patterns}pat[$i]=\"${patterns[$i]}\""
	done
	mopts="$(awk 'BEGIN{matches=0;'"${awk_patterns}"'};NR == 11 {FS=",";$0=$4;'\
'for(i=1;i<=NF;i++)for(j=0;j<2;j++){if($i==pat[j]){result[matches]=$i;matches++;break}}};'\
'END{for(i=0;i<matches;i++){if(i!=0)printf ",";printf result[i]};print ""}' /etc/fstab)";rc_awk=$?

	# setting up the loop device
	passphrase="${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}"\
"${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}${RANDOM}"
	test -z "$passphrase" && { echo "passphrase could not be generated; aborting.";
		rc_failed;rc_exit; }
	echo "$passphrase" | losetup -p 0 -e "$encryption" -C 1 "$loop_device" "$device" &>/dev/null;rc_losetup=$?
	test 0 -ne "$rc_losetup" && { echo "losetup failed; aborting.";
		rc_failed;rc_exit; }
	
	# create the file system
	case "$filesystem" in
		ext2)
			mke2fs "$loop_device" &>/dev/null;rc_mkfs=$?
		;;
		ext3)
			mke2fs -j "$loop_device" &>/dev/null;rc_mkfs=$?
		;;
		reiserfs)
			mkreiserfs -q "$loop_device" &>/dev/null;rc_mkfs=$?
		;;
		reiser4)
			mkfs.reiser4 --quiet "$loop_device" &>/dev/null;rc_mkfs=$?
		;;
		*)
			echo "The file system '${filesystem}' is not supported by this script; aborting."
			rc_failed
			rc_exit
		;;
	esac
	
	# mount the file system to /tmp but don't write to /etc/mtab as this
	#	wouldn't work due to the option "loop"
	test -n "$mopts" && use_mopts="-o ${mopts}" || use_mopts=
	mount -t "$filesystem" -n $use_mopts "$loop_device" "$mount_point" &>/dev/null;rc_mount=$?
	if [ 0 -ne "$rc_mount" ]
		then
		echo "mount failed; aborting"
		losetup -d "$loop_device"
		rc_failed
		rc_exit
	else
		# create the /etc/mtab entry, show the real device and the loop device as mount option
		nec_mtab_mopts="loop=${loop_device},encryption=${encryption}"
		test -n "$mopts" && mtab_mopts="${mopts},${nec_mtab_mopts}" || mtab_mopts="${nec_mtab_mopts}"
		mount -t "$filesystem" -f -o "$mtab_mopts" "$device" "$mount_point"
		mkdir /tmp/.ICE-unix
		chmod -R 777 /tmp
		chmod -R o+t /tmp
	fi
	
	# Remember status and be verbose
	rc_status -v
	;;
    stop)
	echo -n "Unmounting randomly encrypted '${mount_point}' "
	umount "${mount_point}"

	# Remember status and be verbose
	rc_status -v
	;;
    restart)
	## Stop the service and regardless of whether it was
	## running or not, start it again.
	$0 stop
	$0 start

	# Remember status and be quiet
	rc_status
	;;
    status)
	echo -n "Checking for encrypted volume for ${mount_point} "
	encryption_active=yes
	while true # use a pseudo loop so that we can leave it from everywhere like a function
		do
		# check if mount point is already in use
		awk '$2 == "'"${mount_point}"'" {found=1};END{if(found==1)exit(0);else exit(1)}' /proc/mounts ||
			{ encryption_active=no;break; }
		device="$(awk '$1 ~ "^/dev/" && $2 ~ "^'"${mount_point}"'/?$" {print $1;matched++};'\
'END{if(matched==0) exit(1); else if(matched==1) exit(0);else exit(2)}' /proc/mounts)" ||
			{ encryption_active=no;break; }
		losetup "$device" &>/dev/null || 
			{ encryption_active=no;break; }
		losetup -a | grep -qE "^${device}.*encryption" ||
			{ encryption_active=no;break; }
		break
	done

	# Return value is slightly different for the status command:
	# 0 - service up and running
	# 3 - service not running (unused)
	
	test yes = "$encryption_active" || bash -c "exit 3"
	
	# NOTE: rc_status knows that we called this init script with
	# "status" option and adapts its messages accordingly.
	rc_status -v
	;;
    *)
	echo "Usage: $0 {start|stop|status}"
	exit 1
	;;
esac
rc_exit

