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

hopeless_linux: RawNotes/encrypted harddrive script (last modified 2007-07-01 16:01:00)