#!/bin/bash

set -e

# Copyright (C) 2016 by Mike Gabriel <mike.gabriel@it-zukunft-schule.de>

# This script is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

# This script updates the krb5 host keytabs for a list of given hosts
# in /var/lib/debian-edu/dlw-keytabs for all hosts that are members
# in the NIS netgroup 'diskless-workstation-hosts'.
#
# The host keytab files are stored with read permissions for the
# debian-edu system user.
#
# In a diskless workstation chroot (aka LTSP fat client), make sure
# that the diskless system can copy over its own host keytab file
# via
#
#     scp debian-edu@tjener.intern:/var/lib/debian-edu/dlw-keytabs/$HOSTNAME.keytab /etc/krb5.keytab
#
# This line can be put into /etc/rc.local, for exmample. SSH private
# and public key files need to be in place correctly to make this
# work.
#
# This provides the possibility to use NFSv4 and Kerberos krb5i
# authentication from a diskless machine against the NFS server
# on the Debian Edu mainserver.

DOMAIN="intern"

SPECIAL_USER="debian-edu"
SPECIAL_GROUP="${SPECIAL_USER}"

DLW_KRB5_KEYTABS_DIR="/var/lib/debian-edu/dlw-keytabs"

# Clear caching daemon's NIS netgroup cache (this assures an LDAP re-lookup).
nscd -i netgroup
DLW_HOSTS_NETGROUP="$(netgroup diskless-workstation-hosts | grep -E "\.${DOMAIN}$")" || true

# Do some sanity checks...
if [ "$(id -u)" != "0" ]; then
	echo "ERROR: This script must be run as super-user root"
	exit 1
elif ! getent passwd ${SPECIALUSER} 1>/dev/null; then
	echo "ERROR: This script requires the debian-edu system user account"
	exit 1
elif ! getent group ${SPECIAL_GROUP} 1>/dev/null; then
	echo "ERROR: This script requires the debian-edu system group"
	exit 1
elif [ -z "${DLW_HOSTS_NETGROUP}" ]; then

	# FIXME: differentiate between diskless-workstation-hosts not present or empty!

	echo "NOTICE: NIS netgroup 'diskless-workstation-hosts' not found. Nothing to do."
	exit 0
fi

DLW_HOSTS=""

# obtain DLW_HOSTS from NIS Netgroup or from the command line
if [ -z "${1}" ]; then
	DLW_HOSTS="${DLW_HOSTS_NETGROUP}"
else
	logger -t update-dlw-krb5-keytabs -p notice "Called with command line: ${@}"

	while [ -n "${1}" ]; do
		if echo ${DLW_HOSTS_NETGROUP} | grep -q "${1}.${DOMAIN}"; then
			DLW_HOSTS="${DLW_HOSTS} ${1}.${DOMAIN}"
		else
			echo "WARNING: Host ${1} not a diskless workstation"
			logger -t update-dlw-krb5-keytabs -p warning "Host '${1}' is not a diskless workstation."
		fi
		shift
	done
fi

mkdir -p "${DLW_KRB5_KEYTABS_DIR}"
chown "root:${SPECIAL_USER}" "${DLW_KRB5_KEYTABS_DIR}"
chmod 0710 "${DLW_KRB5_KEYTABS_DIR}"

for dlw_host in ${DLW_HOSTS}; do

	DLW_KRB5_KEYTAB="${DLW_KRB5_KEYTABS_DIR}/${dlw_host}.keytab"

	host_found="false"
	ldap_cn=$(echo ${dlw_host} | cut -d"." -f1)

	ldap_host=""

	while read KEY VALUE; do
		case "$KEY" in
			dn:)
				ldap_host=""
				;;
			cn:)
				ldap_host="$VALUE"
				if [ "${ldap_host}.${DOMAIN}" = "${dlw_host}" ]; then
					host_found="true"
				else
					continue
				fi

				if LANG=C kadmin.local -q "get_principal host/${dlw_host}" 2>/dev/null  | grep -q "^Principal: host/${dlw_host}@.*" &&
				   LANG=C kadmin.local -q "get_principal nfs/${dlw_host}" 2>/dev/null  | grep -q "^Principal: nfs/${dlw_host}@.*" ; then

					kadmin.local -q "ktadd -k ${DLW_KRB5_KEYTAB}.new host/${dlw_host}"
					kadmin.local -q "ktadd -k ${DLW_KRB5_KEYTAB}.new nfs/${dlw_host}"

					chown "root:${SPECIAL_USER}" "${DLW_KRB5_KEYTAB}.new"
					chmod 0640 "${DLW_KRB5_KEYTAB}.new"
					mv -v "${DLW_KRB5_KEYTAB}.new" "${DLW_KRB5_KEYTAB}"
					cp -av "${DLW_KRB5_KEYTAB}" "${DLW_KRB5_KEYTAB/.${DOMAIN}/}"
				else
					echo "WARNING: Diskless workstation '${dlw_host}' is missing a host (host/${dlw_host}) or service (nfs/${dlw_host}) principal in the Kerberos database."
					logger -t update-dlw-krb5-keytabs -p warning "Diskless workstation '${dlw_host}' is missing a host (host/${dlw_host}) or service (nfs/${dlw_host}) principal in the Kerberos database."
				fi
				break
				;;
			*)
				;;
		esac
	done <<< `ldapsearch -xLLL "(&(cn=$ldap_cn)(|(objectClass=GOHard)(objectClass=ipHost)))" cn 2>/dev/null | perl -p00e 's/\r?\n //g'`

	if [ "$host_found" != "true" ]; then

		# if we land here, three things might have happened:
		#
		#   1. this script is called from gosa-remove-host (and we need to clean up the keytab file)
		#   2. this script has been called with a wrong hostname (one that does not exist in LDAP)
		#   3. this script has found a DLW entry in NIS netgroup 'diskless-workstation-hosts' that
		#      does not exist in LDAP (any more). Manual tidying up is required in that case.

		if [ -f "${DLW_KRB5_KEYTAB}" ]; then
			logger -t update-dlw-krb5-keytabs -p info "Cleaning up DLW keytab file of host '${dlw_host}'."
			rm -v "${DLW_KRB5_KEYTAB}"
			rm -v "${DLW_KRB5_KEYTAB/.${DOMAIN}/}"
		elif [ -f "${DLW_KRB5_KEYTAB/.${DOMAIN}/}" ]; then
			logger -t update-dlw-krb5-keytabs -p info "Cleaning up leftover DLW keytab file of host '${dlw_host}' (without domain part)."
			rm -v "${DLW_KRB5_KEYTAB/.${DOMAIN}/}"
		else

			echo "WARNING: Hostname '${dlw_host}' listed in NIS netgorup 'diskless-workstation-hosts', but not found as a host entry in Debian Edu LDAP."
			logger -t update-dlw-krb5-keytabs -p warning "Hostname '${dlw_host}' listed in NIS netgorup 'diskless-workstation-hosts', but not found as a host entry in Debian Edu LDAP."

		fi

	fi

done

# FIXME: count updated files / hosts
logger -t update-dlw-krb5-keytabs -p notice "Diskless workstation Krb5 keytab files updated."

exit 0
