#! /bin/bash
# $Header: /global/src/usr.bin/src/RCS/update-authorized-keys,v 1.11 2012/02/28 20:45:19 pb22 Exp $

# Forked from  /usr/groups/linux/suse/usr-local-bin/update-authorized-keys
# to avoid breaking SUSE systems

# copy user's authorized-keys from filer to the local FS so that it can be
# accessed when there is no krb5 ticket, allowing sshd to auth the user

# Dropped full SUSE ($VSS/%u/authorized_keys) support, so no need to run as root

getent=/usr/bin/getent
stat=/usr/bin/stat
mktemp=/bin/mktemp
cp=/bin/cp
chown=/bin/chown
umaskarg=022
chmod=/bin/chmod
mode=0400
VSS=/var/spool/ssh
LS=/local/scratch
CHOWNDIR=y
CPF=

sshdconf=/etc/ssh/sshd_config
secparg="Zsystem_u:object_r:nfs_t:s0"	# extra argument needed for SELinux

# Check if SELinux cp works
TESTFILE=$($mktemp -t TEST-CP.XXXXXXXX)
$cp -p$secparg /etc/issue $TESTFILE > /dev/null 2>&1
test -s $TESTFILE || secparg=""
rm -f $TESTFILE

if [ -z "$1" ] ; then
   LOGIN=${SUDO_USER-$USER}
else
   LOGIN=$1
fi

# Look in sshd_config (if readable) to see where sshd looks for local authorized_keys files
unset DEST DESTD SPOOLDIR
if test -r $sshdconf
then
  for pat in AuthorizedKeysFile AuthorizedKeysFile2
  do   case "$DEST" in
    '')	AKF=`sed -n "s/^$pat[ 	][ 	]*//p" $sshdconf 2> /dev/null`
	case "$AKF" in
	$VSS/%u)			# anyone can setup if already created
		CHOWNDIR=n CPF=f DESTD=$VSS
		DEST="$AKF";;
	$LS/%u/.ssh/authorized_keys)	# anyone can setup
		DEST="$AKF";;
	$VSS/%u/authorized_keys)	# registered user can setup
		DEST="$AKF";;
	'')	: Not found;;
	*)	echo "$0: expected '$pat $AKF' ignored" 1>&2;;
	esac
    esac
  done
else
  # Use sudo to look in sshd_config, if it is so configured
  case "`sudo -l`" in
  *"NOPASSWD: /bin/grep ^AuthorizedKeysFile /etc/ssh/sshd_config"*)
  for pat in AuthorizedKeysFile AuthorizedKeysFile2
  do   case "$DEST" in
    '')	AKF=`sudo grep ^AuthorizedKeysFile $sshdconf|sed -n "s/^$pat[ 	][ 	]*//p" 2> /dev/null`
	case "$AKF" in
	$VSS/%u)			# anyone can setup if already created
		CHOWNDIR=n CPF=f DESTD=$VSS
		DEST="$AKF";;
	$LS/%u/.ssh/authorized_keys)	# anyone can setup
		DEST="$AKF";;
	$VSS/%u/authorized_keys)	# registered user can setup
		DEST="$AKF";;
	'')	: Not found;;
	*)	echo "$0: expected '$pat $AKF' ignored" 1>&2;;
	esac
    esac
  done
  ;;
  *) echo no sudo access to /etc/ssh/sshd_config
     DEST=UnReadable
  esac
fi

case "$DEST" in
# No AuthorizedKeysFile{,2} found, so warn user and default
UnReadable|'')
	case "$DEST" in
	UnReadable) echo "$0: $sshdconf not readable" 1>&2;;
	'')	echo "$0: no expected AuthorizedKeysFile* found in $sshdconf" 1>&2;;
	esac
	case "`cat /etc/issue`" in
	*SUSE*|*SuSE*)
		SPOOLDIR=$VSS
		DESTD=$SPOOLDIR/$LOGIN
		DEST=$DESTD/authorized_keys
		;;
	*)	# (e.g.) TSS use $VSS to hold a full set of user keys
		if	test -d $VSS
		then
			SPOOLDIR=$VSS
			DESTD=$SPOOLDIR
			DEST=$DESTD/$LOGIN
			CHOWNDIR=n
			CPF=f
			if	test ! -r $DEST
			then	echo "No existing '$DEST' - run "cl-onserver --fixsshcache" to do a full rebuild" >&2
				exit 1
			fi
		else
			SPOOLDIR=$LS
			DESTD=$SPOOLDIR/$LOGIN/.ssh
			DEST=$DESTD/authorized_keys
		fi
	esac
	echo "	defaulting to $DEST" 1>&2
	;;
# AuthorizedKeysFile{,2} found, so process to generate required variables
*)	DEST=`echo "$DEST" | sed "s/%u/$LOGIN/"`
	case "$DESTD" in '') DESTD=`echo "$DEST" | sed "s/\/authorized_keys$//"`;; esac
	case "$SPOOLDIR" in '') SPOOLDIR=`echo "$DESTD" | sed "s/\/$LOGIN$//;s/\/.ssh$//"`;; esac
	;;
esac

function exit_trap () {
   if [ -n "$TEMPFILE" -a -e "$TEMPFILE" ] ; then
      echo "$0: deleting $TEMPFILE" >&2
      rm $TEMPFILE
   fi
}

if [ $EUID != 0 -o "x$SUDO_USER" = "x" ] ; then 
   case "$SPOOLDIR" in
   /var*)
	if	test ! -r $DEST
	then	echo "$0: error: you must run this under sudo. Type" >&2
		echo >&2
		echo "         sudo $0" >&2
		echo >&2
		exit 1;
   	fi
   esac
fi

$getent passwd $LOGIN > /dev/null 2>&1
if [ $? != 0 ] ; then
    echo "$0: error: $LOGIN does not exist in password database." >&2
    exit 3
fi

if [ ! -e $SPOOLDIR ] ; then
   case "$SPOOLDIR" in
	/var/spool/*)	# needs special privs
   		echo "$0: creating $SPOOLDIR" >&2
   		(umask $umaskarg; mkdir -p $SPOOLDIR)
		;;
	$LS/*) # anyone can do
   		echo "$0: create $SPOOLDIR using:" >&2
   		echo "	sudo mkscratchdir" >&2
		exit 1;;
	*)	echo "$0: unexpected SPOOLDIR '$SPOOLDIR' - please contact sys-admin@cl.cam.ac.uk"  >&2
		exit 1;;
	esac
fi

if [ ! -e $SPOOLDIR ] ; then
   echo "$0: error: $SPOOLDIR doesn't exist, can't continue." >&2
   exit 2
fi

if [ ! -e $DESTD ] ; then
   case "$DESTD" in
	$LS/*) # anyone can do
		if [ $EUID = 0 -a "x$SUDO_USER" != "x" ] ; then 
   			echo "$0: create $DESTD using:" >&2
   			echo "	(umask $umaskarg; mkdir -p $DESTD)" >&2
			exit 1
		fi;
   esac
   case "$DESTD" in
	$LS/*|/var/spool/*)
   		echo "$0: creating $DESTD" >&2
   		(umask $umaskarg; mkdir -p $DESTD)
	;;
	*)	echo "$0: unexpected DESTD '$DESTD' - please contact sys-admin@cl.cam.ac.uk"  >&2
		exit 1;;
	esac
fi

if [ ! -e $DESTD ] ; then
   echo "$0: error: $DESTD doesn't exist, can't continue." >&2
   exit 2
fi
case "$CHOWNDIR" in
y*)	$chown $LOGIN $DESTD || {
   echo "$0: error: chown to '$LOGIN' of file '$DESTD' failed." >&2
   exit 2
}
esac

AUTHKEYS=/home/$LOGIN/.ssh/authorized_keys
if [ ! -r $AUTHKEYS ] ; then
   echo "$0: error: Can't read $AUTHKEYS. Copy using" >&2
   echo "	cp -p$secparg $AUTHKEYS $DEST" >&2
   exit 2
fi

MAXSIZE=10240
TEMPFILE=$($mktemp -t "ssh-authkeys-spool.XXXXXXXX") && {
   trap exit_trap EXIT
   grep -v ^# < $AUTHKEYS > $TEMPFILE || {
      ERROR=$?
      echo "$0: error: cp failed copying $AUTHKEYS (error $ERROR)"
      exit 4
   }
   touch -r $AUTHKEYS  $TEMPFILE
   SIZE=$($stat -c "%s" $TEMPFILE) && {
      if [ $SIZE -gt $MAXSIZE ] ; then
         echo "$0: error: $AUTHKEYS is larger than $MAXSIZE bytes." >&2
   	rm $TEMPFILE
         exit 2
      fi
   } 

   # If the destination exists, check if they are identical,
   # and make the destination writable.
   if	test -e $DEST
   then
      if	diff  $TEMPFILE $DEST
      then	echo Files appear identical - no update needed. >&2
   	    rm $TEMPFILE
   	    exit 1
      fi
      chmod u+w $DEST || {
          echo "$0: error: chmod u+w of file '$DEST' failed." >&2
      }
   fi
   $cp -p$CPF$secparg $TEMPFILE $DEST || {
      echo "$0: error: copy from '$TEMPFILE' to '$DEST' failed." >&2
      rm $TEMPFILE
      exit 2
   }
   $chown $LOGIN $DEST || {
      echo "$0: error: chown to '$LOGIN' of file '$DEST' failed." >&2
      rm $TEMPFILE
      exit 2
   }
   $chmod $mode $DEST || {
      echo "$0: error: chmod to '$mode' of file '$DEST' failed." >&2
   }
   rm $TEMPFILE
   echo "$AUTHKEYS installed as $DEST."
   exit 0
}

