#!/bin/bash

export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/puppetlabs/bin

cryptsetup="$(command -v cryptsetup)"
crypttab='/etc/crypttab'

# -- functions ----------------------------------------------------------------

msg() {
  if [ -n "${1:-}" ]; then
    echo -e "$1"
  fi
}

warn() {
  msg "${1:-}" 1>&2
}

fail() {
  warn "${1:-}"
  exit "${2:-1}"
}

key_invalid() {
  local key="${1:-}"
  if [ -z "$key" ] || [ "$key" = 'none' ]; then
    return 1
  fi
  if [ "$key" = 'random' ]; then
    warn 'LUKS does not work with a random key'
    return 0
  fi
  if [ ! -e "$key" ]; then
    warn "keyfile ${key} not found"
    return 0
  fi
  if [ "$(stat -Lc "%U" "$key")" != 'root' ]; then
    warn "insecure owner for ${key}, see /usr/share/doc/cryptsetup/README.Debian"
    return 0
  fi
  if [ "$(stat -Lc "%G" "$key")" != 'root' ]; then
    warn "insecure group for ${key}, see /usr/share/doc/cryptsetup/README.Debian"
    return 0
  fi
  if [ "$(stat -Lc "%A" "$key" | cut -c 5-)" != '------' ]; then
    warn "insecure permissions for ${key}, see /usr/share/doc/cryptsetup/README.Debian"
    return 0
  fi
  return 1
}

start_disk() {
  local name="${1:?missing argument: name}"
  local device="${2:?missing argument: device}"
  local key="${3:?missing argument: key}"

  # resolve device UUID or label
  if [ "${device#UUID=}" != "$device" ]; then
    device="/dev/disk/by-uuid/${device#UUID=}"
  elif [ "${device#LABEL=}" != "$device" ]; then
    device="/dev/disk/by-label/${device#LABEL=}"
  fi

  if key_invalid "$key"; then
    warn "invalid key: ${key}"
    return 1
  fi

  if [ ! -r "$device" ]; then
    warn "${name} skipped, device ${device} does not exist"
    return 1
  fi

  if [ ! -e /dev/mapper/"$name" ]; then
    cryptsetup luksOpen --key-file="$key" "$device" "$name"
    return "$?"
  fi
  return 0
}

stop_disk() {
  local name="${1:?missing argument: name}"
  if [ -e /dev/mapper/"$name" ]; then
    cryptsetup luksClose "$name"
  fi
}

check_disk() {
  local name="${1:?missing argument: name}"
  if [ ! -e /dev/mapper/"$name" ]; then
    exit 1
  fi
}

# -- sanity checks ------------------------------------------------------------

# crypttab missing or empty => nothing to do
if [ ! -f "$crypttab" ]; then
  exit 0
fi
crypttab_data="$(grep -Ev '^[[:space:]]*(#|$)' "$crypttab")"
if [ -z "$crypttab_data" ]; then
  exit 0
fi

# crypttab not empty but cryptsetup missing or not executable
if [ -z "$cryptsetup" ]; then
  fail 'missing cryptsetup binary'
fi
if [ ! -x "$cryptsetup" ]; then
  fail "${cryptsetup} is not executable"
fi

# -- main ---------------------------------------------------------------------

case "$1" in
  start)  action='start_disk';;
  stop)   action='stop_disk';;
  status) action='check_disk';;
  *)      echo "action not recognized: ${1}" 1>&2; exit 1;;
esac

rc='0'
echo "$crypttab_data" | while read -r name device key opts; do
  if [ "$opts" == 'luks' ]; then
    if ! "$action" "$name" "$device" "$key"; then
      ((rc++)) || true
    fi
  else
    warn "ignoring crypttab entry: ${name} ${device} ${key} ${opts}"
  fi
done

if [ "$rc" -ne 0 ]; then
  fail 'cryptdisk setup completed with errors' "$rc"
fi

exit 0
