Author
Christopher Marshall (christopherlmarshall@yahoo.com)
A Script for Managing Encrypted Hard Drives
#!/bin/bash
# this script is for the setup and usage of an external usb drive
# organized as an encrypted LVM volume group (VG).
# the Physical Volumes (PVs) of the VG are created by
# crypto mapping the partitions of the hard drive with the 2.6.x device mapper.
configdir=/etc/usbcd/
if ! [ -d /etc/usbcd ] ; then
mkdir /etc/usbcd
fi
active_drive_link=${configdir}/activedrive
if [ -e "$active_drive_link" ] ; then
source "$active_drive_link"
active_drive_set="Y"
else
active_drive_set="N"
fi
function check_active_drive {
if [ $active_drive_set = "N" ] ; then
echo "no active drive set"
exit 1
fi
}
# prompt the user for a password
function get_password {
local prompt=$1
local pass
stty -F /dev/tty -echo
echo -n "$prompt" > /dev/tty
read pass < /dev/tty
stty -F /dev/tty echo
echo $pass
}
function genkey {
# I realize this is a little cryptic, no pun intended.
# /dev/random prints random bits from an entropy pool
# od (octal dump) converts the byte stream to a hex string with an address field,
# followed by space delimited blocks of 4 hex characters.
# the cut command skips past the address field
# the tr command removes spaces to give, finally, a random hex string of 32 characters
# representing 16 random bytes (or 128 random bits).
dd if=/dev/random bs=1 count=16 2>/dev/null \
| od -t x2 \
| head --lines=1 \
| cut -d" " -f2- \
| tr -d " "
}
function getkey {
local dev="$1"
local pass="$2"
if [ -z "$1" ] ; then
echo "getkey: <dev> [pass]" 1>&2
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
check_active_drive
cfsmakedev ${dev}${keyvol} ${keymdev} $pass
dd if=/dev/mapper/${keymdev} bs=32 count=1 2>/dev/null
# I've noticed that the remove fails sometimes if it happens too soon after
# a write to the device.
while ! dmsetup remove ${keymdev} ; do
sleep 1
done
}
function setkey {
local dev="$1"
local pass="$2"
local key="$3"
if [ -z "$1" ] ; then
echo "setkey: <dev> [pass] [key]" 1>&2
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
if [ "$key" == "" ] ; then
key=$(get_password key)
fi
check_active_drive
cfsmakedev ${dev}${keyvol} $keymdev $pass
echo -n "$key" | dd of=/dev/mapper/${keymdev} bs=32 count=1 2>/dev/null
dmsetup remove ${keymdev}
}
function setrkey {
local dev=$1
local pass=$2
if [ -z "$1" ] ; then
echo "setrkey: <dev> [pass]" 1>&2
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
check_active_drive
local key=$(genkey)
setkey $dev $pass $key
}
function setpass {
local dev=$1
local oldpass=$2
local newpass=$3
if [ -z "$1" ] ; then
echo "setpass: <dev> [oldpass] [newpass]" 1>&2
exit 1
fi
if [ "$oldpass" == "" ] ; then
oldpass=$(get_password old_pass)
fi
if [ "$newpass" == "" ] ; then
newpass=$(get_password new_pass)
fi
check_active_drive
setkey $dev $newpass $(getkey $dev $oldpass)
}
function cfsmakedev {
local dev=$1
local mdev=$2
local pass=$3
if [ -z "$2" ] ; then
echo "md <dev> <mdev> [pass]" 1>&2
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
local blksize=$(blockdev --getsize $dev)
local key=$(echo "$pass" | md5sum | cut -d" " -f1)
echo "0 $blksize crypt aes-plain $key 0 $dev 0" | dmsetup create ${mdev}
}
function vgmap {
local dev="$1"
local pass="$2"
if [ -z "$2" ] ; then
echo "usage: vgmap <dev> [pass]"
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
check_active_drive
local key=$(getkey $dev $pass)
for vol in $vols ; do
cfsmakedev ${dev}${vol} ${VG}pv${vol} $key
done
}
function vgunmap {
check_active_drive
for vol in $vols ; do
dmsetup remove /dev/mapper/${VG}pv${vol}
done
}
function vgmount {
local dev="$1"
local dir="$2"
local pass="$3"
if [ -z "$2" ] ; then
echo "vgmount <dev> <dir> [pass]"
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
check_active_drive
vgmap $dev $pass
vgscan
vgchange -a y $VG
mount /dev/${VG}/${LV} $dir
}
function vgumount {
check_active_drive
umount /dev/${VG}/${LV}
lvchange -a n /dev/${VG}/${LV}
vgchange -a n $VG
vgunmap
}
function create {
check_active_drive
local dev="$1"
local pass="$2"
if [ -z "$1" ] ; then
echo "usage: create <dev> [pass]"
exit 1
fi
if [ "$pass" == "" ] ; then
pass=$(get_password pass)
fi
echo "setting random disk key"
setrkey $dev $pass
echo "mapping VG's PVs"
if ! vgmap $dev $pass ; then
echo "vgmap failed"
exit 1
fi
echo "creating PV's"
for vol in $vols ; do
if ! pvcreate /dev/mapper/${VG}pv${vol} ; then
echo "coulnt create PV"
exit 1
fi
done
echo "creating VG"
local PVLIST
for vol in $vols ; do
PVLIST="${PVLIST} /dev/mapper/${VG}pv${vol}"
done
if ! vgcreate $VG $PVLIST ; then
echo "couldnt create VG"
exit 1
fi
echo "activating VG"
if ! vgchange -a y $VG ; then
echo "couldnt activate VG"
exit 1
fi
echo "creating logical volumes"
if ! lvcreate -L${LVSIZE} -n${LV} $VG ; then
echo "couldnt create logical volume"
exit 1
fi
echo "formatting with resierfs"
if ! mkreiserfs -f -f /dev/${VG}/${LV} ; then
echo "couldnt make filesystem"
exit 1
fi
vgchange -a n $VG
vgunmap
}
function list_drives {
if ! [ -e $configdir ] ; then
echo "no drives defined"
exit 1
fi
echo "drives defined in ${configdir}:"
find ${configdir} -type f -print | (
while read file ; do
echo "${file##*/}"
done
)
if [ -e "${active_drive_link}" ] ; then
activedrive=$(stat -c %N ${active_drive_link} | cut -d" " -f3 | tr -d "\`'")
echo "active drive is: ${activedrive##*/}"
else
echo "no active drive selected"
fi
}
function set_drive {
local dname="$1"
if [ -z "$1" ] ; then
echo "usage: sd <drive name>"
exit 1
fi
if [ -e /etc/usbcd/${dname} ] ; then
ln -sf ${dname} ${active_drive_link}
else
echo "$dname doesn't exist"
exit 1
fi
}
if [ -z "$1" ] ; then
echo ""
echo "usbcd (usb crypt drive)"
echo " before this command will work, you need to add this line to your /etc/lvm.conf"
echo " types = [ \"device-mapper\", 16 ]"
echo " in the \"devices\" section. Without it, lvm won't allow pvcreate to operate on /dev/mapper devices"
echo ""
echo "usage: usbcd <command> <arguments>"
echo ""
echo "commands:"
echo " ld "
echo " list available drives"
echo " sd <drive name>"
echo " set drive"
echo " mount <dev> <dir> <pass>"
echo " establish the crypto mappings and mount the logical volume"
echo " umount"
echo " undo everything the mount command did"
echo " create <dev> <pass>"
echo " create the physical volumes, logical volume, and filesystem. This will entirely destroy any data you previously stored in the logical volume so really be careful."
echo " map <dev> <pass>"
echo " (turn on crypto map for lvm vg)"
echo " umap"
echo " (turn off crypto map for lvm vg)"
echo " genkey"
echo " generate a random hex key"
echo " getkey: <dev> <pass>"
echo " get key from device"
echo " setkey: <dev> <key> <pass>"
echo " set device key"
echo " setrkey: <dev> <key> <pass>"
echo " set device key randomly"
echo " setpass: <dev> <oldpass> <newpass>"
echo " reencrypt disk key with new passphrase"
exit 1
fi
command="$1"
shift
if [ "$command" == "ld" ] ; then
list_drives "$@"
elif [ "$command" == "sd" ] ; then
set_drive "$@"
elif [ "$command" == "mount" ] ; then
vgmount "$@"
elif [ "$command" == "umount" ] ; then
vgumount "$@"
elif [ "$command" == "map" ] ; then
vgmap "$@"
elif [ "$command" == "umap" ] ; then
vgunmap "$@"
elif [ "$command" == "create" ] ; then
create "$@"
elif [ "$command" == "genkey" ] ; then
genkey "$@"
elif [ "$command" == "getkey" ] ; then
getkey "$@"
elif [ "$command" == "setkey" ] ; then
setkey "$@"
elif [ "$command" == "setrkey" ] ; then
setrkey "$@"
elif [ "$command" == "setpass" ] ; then
setpass "$@"
elif [ "$command" == "md" ] ; then
cfsmakedev "$@"
else
echo "\"$command\" is not a valid command for usbcd"
exit 1
fi
