#!/bin/bash

sshcs_check_nonempty() {
  local filepath="$1"

  [ -e "${filepath}" ] && grep -q -v '^\s*\(#\|$\)' "${filepath}"
}

sshcs_check_keys() {
  local dropbear_keyfile
  local openssh_keyfile
  local fingerprint

  for keytype in "${dropbear_key_types[@]}"; do
    dropbear_keyfile=${dropbear_keyfile_prefix}${keytype}${dropbear_keyfile_suffix}
    openssh_keyfile=${openssh_keyfile_prefix}${keytype}${openssh_keyfile_suffix}

    # Prefer OpenSSH keys, or generate missing ones
    if [ -e "${openssh_keyfile}" ]; then
      #echo "Copying OpenSSH ${keytype} host key for dropbear ..."
      dropbearconvert openssh dropbear "${openssh_keyfile}" "${dropbear_keyfile}" > /dev/null 2>&1
    elif [ ! -e "${dropbear_keyfile}" ]; then
      #echo "Generating ${keytype} host key for dropbear ..."
      dropbearkey -t "${keytype}" -f "${dropbear_keyfile}" > /dev/null 2>&1
    fi
    fingerprint=$(dropbearkey -y -f "${dropbear_keyfile}" | sed -n '/^Fingerprint:/ {s/Fingerprint: *//; p}')
    echo "$(basename "${dropbear_keyfile}") : ${fingerprint}"
  done
}

build() {
  local etc_crypttab="/etc/crypttab"
  local dropbear_authorized_keys="/etc/dropbear/initrd.authorized_keys"
  local sshcs_env="/etc/initcpio/sshcs_env"
  local dropbear_key_types=( "rsa" "ecdsa" "ed25519" )
  local dropbear_keyfile_prefix="/etc/dropbear/dropbear_"
  local dropbear_keyfile_suffix="_host_key"
  local openssh_keyfile_prefix="/etc/ssh/ssh_host_"
  local openssh_keyfile_suffix="_key"

  # Check we are needed
  if ! sshcs_check_nonempty "${dropbear_authorized_keys}"; then
    echo "There is no root key(s) in ${dropbear_authorized_keys}. Skipping."
    return 0
  fi
  if ! sshcs_check_nonempty "${etc_crypttab}"; then
    echo "There is no device in ${etc_crypttab}. Skipping."
    return 0
  fi

  umask 0022

  sshcs_check_keys

  # Note: parts of this script (modules/binaries/files added) are the same than
  # other install scripts (/usr/lib/initcpio/install/):
  #   - 'encryp': nominal support of encrypted volumes at boot time
  #   - 'net': network tools

  ## Modules
  # (from 'encrypt')
  add_module 'dm-crypt'
  add_module 'dm-integrity'
  if [[ $CRYPTO_MODULES ]]; then
    local mod
    for mod in $CRYPTO_MODULES; do
      add_module "$mod"
    done
  else
    add_all_modules '/crypto/'
  fi

  # (from 'net')
  add_checked_modules '/drivers/net/'


  ## Binaries
  # (from 'encrypt')
  add_binary 'cryptsetup'
  # cryptsetup calls pthread_create(), which dlopen()s libgcc_s.so.1
  # Note: at least necessary for LUKS v2 volumes.
  # Also see similar/related bug reports (e.g. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=950254).
  add_binary '/usr/lib/libgcc_s.so.1'

  # (from 'net')
  add_binary '/usr/lib/initcpio/ipconfig' '/bin/ipconfig'

  # (ours)
  # Note: dmsetup is necessary for device mapper features
  add_binary 'dmsetup'
  add_binary 'dropbear'
  add_binary 'ip'
  add_binary 'ethtool'


  ## Other files
  # (from 'encrypt')
  # cryptsetup-related files
  map add_udev_rule \
      '10-dm.rules' \
      '13-dm-disk.rules' \
      '95-dm-notify.rules'

  # (ours)
  # Our script and options
  [ -e "${sshcs_env}" ] && add_file "${sshcs_env}"
  # Note: use /usr/local/bin, even though everything actually points to /usr/bin
  # in initramfs.
  add_file '/usr/lib/initcpio/hooks/ssh-cryptsetup-tools' '/usr/local/bin/ssh-cryptsetup-tools'

  # SSH-related files
  add_file "${dropbear_authorized_keys}" '/root/.ssh/authorized_keys'
  for keytype in "${dropbear_key_types[@]}"; do
    add_file "${dropbear_keyfile_prefix}${keytype}${dropbear_keyfile_suffix}"
  done

  # crypt partitions
  add_file "${etc_crypttab}"

  add_runscript
}

help() {
  cat <<EOF
This hook allows for LUKS encrypted devices to be unlocked either locally
(boot console) or remotely over SSH.

Network is configured with 'ip=' kernel parameter (see 'mkinitcpio-nfs-utils').
Authorized SSH key(s) must be present in '/etc/dropbear/initrd.authorized_keys'.
LUKS encrypted devices to unlock are derived from '/etc/crypttab', which must
be present.
Some options can be set in '/etc/initcpio/sshcs_env' (file is sourced in
initrd shell):
 * 'sshcs_opt_debug': whether to be more verbose about ongoing actions
   - default: '0'
   - any non-zero value to enable
 * 'sshcs_opt_net_wol': Wake-on-LAN option to set on network device
   - default: 'g' (MagicPacket™)
   - usually WOL is disabled once in initramfs shell
   - set empty to not change network device WOL setting
 * 'sshcs_opt_timeout_ipconfig': time (in seconds) to configure IP
   - default: '10'
 * 'sshcs_opt_listen': SSH listening port
   - default: '22'
 * 'sshcs_opt_timeout_poweroff': time (in seconds) to unlock devices before
   automatic powering off
   - default (and minimum value): '120' (2 minutes)
   - negative value to deactivate
 * 'sshcs_opt_use_shell': whether to start a full 'ash' shell
   - default: '0'
   - '1' to enable
   - when disabled (the default), a script to unlock devices is executed instead

Each SSH server key ('dropbear_rsa_host_key', 'dropbear_ecdsa_host_key' and
'dropbear_ed25519_host_key' in '/etc/dropbear' folder) is imported from OpenSSH
if present or generated if missing. Fingerprints are displayed upon building
the initramfs image.
EOF
}
