#!/bin/bash
#
# An SSH command wrapper script to require use of YubiKey
# *in addition* to existing configured SSH authentication
#
# Author: Daniel P. Berrange
#
# Derived from script by Alexandre De Dommelin:
#
# http://www.tuxz.net/blog/archives/2010/03/17/how_to_quickly_setup_two-factor_ssh_authentication/
#
# Motivation is to allow use of SSH public key auth together
# with YubiKey auth, which not otherwise possible since
# once a user has authenticated with a public key, SSH skips
# the PAM authentication phase.
#
# 1. Save as /sbin/yubikey_shell
#
#      chmod +x /sbin/yubikey_shell
#
# 2. Create /etc/yubikey_shell containing
#
#      YUBICO_API_ID=XXXX
#
#    Where XXXX is the 4 digit ID you got from
#
#      https://upgrade.yubico.com/getapikey/
#
# 2. Create /etc/yubikey_mappings containing lines:
#
#      USERNAME:YUBI_KEY_ID:YUBI_KEY_ID:...
#
# 3. Add the following 2 lines to the end of /etc/ssh/sshd_config
#
#     Match User USERNAME1 USERNAME2 USERNAME3 ...
#       ForceCommand /sbin/yubikey_shell
#
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.

DEBUG=0
TRUSTED_KEYS_FILE=/etc/yubikey_mappings
# This default works, but you really want to use your
# own ID for greater security
YUBICO_API_ID=16

test -f /etc/yubikey_shell && source /etc/yubikey_shell

STD="\\033[0;39m"
OK="\\033[1;32m[i]$STD"
ERR="\\033[1;31m[e]$STD"

##################################################
## Disconnect clients trying to exit the script ##
##################################################
trap disconnect INT

disconnect() {
  sleep 1
  kill -9 $PPID
  exit 1
}

debug() {
  if test "$DEBUG" = 1 ; then
    echo -e "$@"
  fi
}

if test -z "$USER"
then
  debug "$ERR USER environment variable is not set" > /dev/stderr
  disconnect
fi  

####################################
## Get user-trusted yubikeys list ##
####################################
if [ ! -f $TRUSTED_KEYS_FILE ]
then
  debug "$ERR Unable to find trusted keys list" > /dev/stderr
  disconnect
fi

TRUSTED_KEYS=`grep "${USER}:" $TRUSTED_KEYS_FILE | sed -e "s/${USER}://" | sed -e 's/:/\n/g'`
for k in $TRUSTED_KEYS
do
  debug "$OK Possible key '$k'"
done

#######################################
## Get the actual OTP                ##
#######################################

echo -n "Please provide Yubi OTP: "
read -s RAWOTP
echo

# Delete any chars outside range a-z to avoid funny games
# with the URI below
OTP=`echo "$RAWOTP" | tr -c -d a-z`
KEY_ID=${OTP:0:12}

#######################################
## Iterate through trusted keys list ##
#######################################
for trusted in ${TRUSTED_KEYS[@]}
do
  if test "$KEY_ID" = "$trusted"
  then
    debug "$OK Found key in $TRUSTED_KEYS_FILE - validating OTP now ..."
    if wget "https://api.yubico.com/wsapi/verify?id=$YUBICO_API_ID&otp=$OTP" -O - 2> /dev/null | grep "status=OK" > /dev/null
    then
      debug "$OK OTP validated"
      if test -z "$SSH_ORIGINAL_COMMAND"
      then
        exec `grep "^$(whoami)" /etc/passwd | cut -d ":" -f 7`
      else
        exec "$SSH_ORIGINAL_COMMAND"
      fi
      debug "$ERR failed to execute shell / command" > /dev/stderr
      disconnect
    else
      debug "$ERR Unable to validate generated OTP" > /dev/stderr
      disconnect
    fi
  fi
done
debug "$ERR Key not trusted" > /dev/stderr
disconnect
