#!/bin/bash
# Open any volumes created by cryptsetup.
#
# Some notes on /etc/crypttab in Slackware:
# Only LUKS formatted volumes are supported (except for swap)
# crypttab follows the following format:
# <luks_name> <device> <password> <options>
#
# <luks_name>:  This is the name of your LUKS volume.
# For example:  crypt-home
#
# <device>:  This is the device containing your LUKS volume.
# For example:  /dev/sda2
#
# <password>:  This is either the volume password in plain text, or the name of
# a key file.  Use 'none' to interactively enter password on boot.
#
# <options>:  Comma-separated list of options.  Note that there must be a
# password field for any options to be picked up (use a password of 'none' to
# get a password prompt at boot).  The following options are supported:
#
# discard -- this will cause --allow-discards to be passed to the cryptsetup
# program while opening the LUKS volume.
#
# ro -- this will cause --readonly to be passed to the cryptsetup program while
# opening the LUKS volume.
#
# swap -- this option cannot be used with other options.  The device given will
# be formatted as a new encrypted volume with a random key on boot, and used as
# swap.
#
# keyscript=<path/to/script> -- get the password from the named script's stdout.
# The only parameter sent to script is the <password> field, but the script can
# ignore it.
#

luks_start() {
  if [ -f /etc/crypttab -a -x /sbin/cryptsetup ]; then
    # First, check for device-mapper support.
    if ! grep -wq device-mapper /proc/devices ; then
      # If device-mapper exists as a module, try to load it.
      # Try to load a device-mapper kernel module:
      /sbin/modprobe -q dm-mod
    fi
    # NOTE: we only support LUKS formatted volumes (except for swap)!
    # The input for this loop comes from after the "done" below, so that we can
    # use fd3 and keep stdin functional for password entry or in case a keyscript
    # requires it:
    while read line <&3; do
      eval LUKSARRAY=( $line )
      LUKS="${LUKSARRAY[0]}"
      DEV="${LUKSARRAY[1]}"
      PASS="${LUKSARRAY[2]}"
      OPTS="${LUKSARRAY[3]}"
      KEYSCRIPT="$(echo $OPTS | sed -n 's/.*keyscript=\([^,]*\).*/\1/p')"
      LUKSOPTS=""
      if echo $OPTS | grep -wq ro ; then LUKSOPTS="${LUKSOPTS} --readonly" ; fi
      if echo $OPTS | grep -wq discard ; then LUKSOPTS="${LUKSOPTS} --allow-discards" ; fi
      # Skip LUKS volumes that were already unlocked (in the initrd):
      /sbin/cryptsetup status $LUKS 2>/dev/null | head -n 1 | grep -q "is active" && continue
      # Skip LUKS volumes that have the "noauto" option in /etc/fstab:
      if echo $OPTS | grep -wq noauto ; then continue ; fi
      if /sbin/cryptsetup isLuks $DEV 2>/dev/null ; then
        if [ -z "${LUKSOPTS}" ]; then
          echo "Unlocking LUKS encrypted volume '${LUKS}' on device '$DEV':"
        else
          echo "Unlocking LUKS encrypted volume '${LUKS}' on device '$DEV' with options '${LUKSOPTS}':"
        fi
        if [ -x "${KEYSCRIPT}" ]; then
  	# A password was outputted by a script
  	${KEYSCRIPT} "${PASS}" | /sbin/cryptsetup ${LUKSOPTS} luksOpen $DEV $LUKS
  	echo
        elif [ -n "${PASS}" -a "${PASS}" != "none" ]; then
          if [ -f "${PASS}" ]; then
            # A password was given a key-file filename
            /sbin/cryptsetup ${LUKSOPTS} --key-file=${PASS} luksOpen $DEV $LUKS
          else
            # A password was provided in plain text
            echo "${PASS}" | /sbin/cryptsetup ${LUKSOPTS} luksOpen $DEV $LUKS
          fi
        else
          # No password was given, or a password of 'none' was given
          /sbin/cryptsetup ${LUKSOPTS} luksOpen $DEV $LUKS
        fi
      elif echo $OPTS | grep -wq swap ; then
        # If any of the volumes is to be used as encrypted swap,
        # then encrypt it using a random key and run mkswap:
        echo "Creating encrypted swap volume '${LUKS}' on device '$DEV':"
        /sbin/cryptsetup --batch-mode --cipher=aes --key-file=/dev/urandom --key-size=256 create $LUKS $DEV
        mkswap /dev/mapper/$LUKS
      fi
    done 3< <(grep -vE '^(#|$)' /etc/crypttab)
  fi
}

luks_stop() {
  # Close any volumes opened by cryptsetup:
  if [ -f /etc/crypttab -a -x /sbin/cryptsetup ]; then
    cat /etc/crypttab | grep -v "^#" | grep -v "^$" | while read line; do
      # NOTE: we only support LUKS formatted volumes (except for swap)!
      LUKS=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f1 -d' ')
      DEV=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f2 -d' ')
      OPTS=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f4 -d' ')
      if /sbin/cryptsetup isLuks $DEV 2>/dev/null ; then
        echo "Locking LUKS crypt volume '${LUKS}':"
        /sbin/cryptsetup luksClose ${LUKS}
      elif echo $OPTS | grep -wq swap ; then
        # If any of the volumes was used as encrypted swap,
        # then run mkswap on the underlying device -
        # in case other Linux installations on this computer should use it:
        echo "Erasing encrypted swap '${LUKS}' and restoring normal swap on ${DEV}:"
        /sbin/cryptsetup remove ${LUKS}
        mkswap $DEV
      fi
    done
  fi
}

luks_status() {
  if [ -f /etc/crypttab -a -x /sbin/cryptsetup ]; then
    RET=0
    while read line; do
      # NOTE: we only support LUKS formatted volumes (except for swap)!
      LUKS=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f1 -d' ')
      cryptsetup status $LUKS | grep 'active'
      STATUS="${PIPESTATUS[0]}"
      if [ "$STATUS" != "0" ]; then
         RET=1
      fi
    done < <(grep -vE '^(#|$)' /etc/crypttab)
    return $RET
  fi
}

case $1 in
  'start')
    luks_start
    ;;
  'stop')
    luks_stop
    ;;
  'status')
    luks_status
    ;;
  *)
    echo "Usage $0 start|stop|status"
    ;;
esac
