#!/bin/bash
#This script will help in setup kerberos 
#This script can be used for both ui as well as command line
. /opt/safesquid/safesquid_params.sh && EXPORT_INI

LOGFILE="/var/log/safesquid/kerberos.log"

exec > >(tee -a "$LOGFILE") 2>&1

set -o pipefail

let ZONE_EXISTS=$(( 0 ))

_KDESTROY=$(which kdestroy)
_KINIT=$(which kinit)
_MSKTUTIL=$(which msktutil) # /usr/local/bin/msktutil
_KTUTIL=$(which ktutil)
_KLIST=$(which klist)

# VERBOSE=1

_AD_USER=""
MY_COMPUTER_NAME="$(hostname -s)"
MY_FQDN="$(hostname -f)"
MY_DOMAIN="$(hostname -d)"
SPN=${SERVICE_PRINCIPAL_NAME%%.*}
REALM=${AD_DOMAIN^^}

LOG()
{
	echo -e "`date`: kerberos: ${FUNCNAME[1]}: ${*}" 
	cat -
}


CHECK_PARAMETERS ()
{

	[ "x${KRB5_CONFIG}" == "x" ] && LOG "KRB5_CONFIG: not specified" && return 1;
	LOG "KRB5_CONFIG: ${KRB5_CONFIG}"
	
	[ "x${KRB5_KTNAME}" == "x" ] && LOG "KRB5_KTNAME: not specified" && return 1;
	LOG "KRB5_KTNAME: ${KRB5_KTNAME}"
	
	[ "x${AD_FQDN}" == "x" ] && LOG "AD_FQDN: not specified" && return 1;
	LOG "AD_FQDN: ${AD_FQDN}"
		
	[ "x${LDAP_USER}" == "x" ] && LOG "LDAP_USER: not provided\n" && return 1;
	LOG "LDAP_USER: ${LDAP_USER}"
	
	[ "x${LDAP_PASSWORD}" == "x" ] && LOG "LDAP_PASSWORD: not provided" && return 1;	
	
	[ "x${AD_DOMAIN}" == "x" ] && LOG "AD_DOMAIN: not provided" && return 1;
	LOG "AD_DOMAIN: ${AD_DOMAIN}"
	
	[ "x${SERVICE_PRINCIPAL_NAME}" == "x" ] && LOG "SERVICE_PRINCIPAL_NAME: not provided" && return 1;
	LOG "SERVICE_PRINCIPAL_NAME: ${SERVICE_PRINCIPAL_NAME}"
	
	[ "x${AD_IP}" == "x" ] && LOG "AD_IP: not provided" && return 1;
	LOG "AD_IP: ${AD_IP}"
	
	return 0;
}


MAKE_REALM_CONF()
{
	KRB_DIR=`dirname ${KRB5_CONFIG}`
	REALM_CONF="${KRB_DIR}/krb_realms/${REALM//./_}.conf"
	[ -f "${REALM_CONF}" ] && LOG "already exists: ${REALM_CONF}" && return 0;
	
	[ ! -d "${KRB_DIR}/krb_realms/" ] && mkdir -p "${KRB_DIR}/krb_realms/"
	
	cat << _EOF > "${KRB_DIR}/krb_realms/${REALM//./_}.conf"

	# Configuration for ${REALM}
	[realms]
	${REALM^^} = {
		kdc = ${AD_FQDN,,}
		kdc = ${AD_FQDN,,}
		admin_server = ${AD_FQDN,,}
	}

	[domain_realm]
		.${REALM,,} = ${REALM^^}
		${REALM,,} = ${REALM^^}

_EOF

}


MAKE_KRB_CONF()
{
	[ -f "${KRB5_CONFIG}" ] && return;

	cat << _EOF >> ${KRB5_CONFIG}
	# This directive tells Kerberos to include all valid configuration files
	# from the specified directory.
	includedir ${KRB_DIR}/krb_realms/

	[libdefaults]
		# General default settings can go here
		default_realm = ${REALM^^}
		dns_lookup_realm = false
		dns_lookup_kdc = false
		ticket_lifetime = 24h
		renew_lifetime = 7d
		forwardable = true

	[logging]
		kdc = FILE:$LOGFILE
		admin_server = FILE:$LOGFILE
		default = FILE:$LOGFILE
		kdc_rotate = 7d  # Rotate logs every 7 days
		admin_server_rotate = 30d # Rotate logs every 30 days

	[loggers]
#		kdc = INFO
#		admin_server = INFO
#		default = INFO
		kdc = DEBUG
		admin_server = DEBUG
		default = DEBUG

_EOF

}


CHECK_DNS_VALIDITY()
{
	local FQDN=$1
	local SERVER=$2
	LOG "${FQDN} with ${SERVER}"
	host -t A ${FQDN} ${SERVER} && return 0;
	LOG "${FQDN} not recognized by ${SERVER}"
	return 1;
}


DNS_ZONE()
{
	cat <<- _EOF
	${AD_DOMAIN,,} { type stub; masters { ${AD_IP};}; forwarders { ${AD_IP};}; };
	_EOF
}

RNDC_DEL_ZONE()
{
	[[ ZONE_EXISTS -le 0 ]] && LOG "skipping deletion .. ${AD_DOMAIN,,} does not exist" && return 0;
	LOG "${AD_DOMAIN,,} with ${AD_IP}"
	
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key delzone -clean ${AD_DOMAIN,,} || return 1; 
	(( ZONE_EXISTS=0 ));
	return 0;
}

RNDC_ADD_ZONE()
{
	[[ ZONE_EXISTS -gt 0 ]] && LOG "skipping .. ${AD_DOMAIN,,} already exists" && return 0;
	LOG "${AD_DOMAIN,,} with ${AD_IP}"
	
	ZONE=`DNS_ZONE`
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key addzone ${ZONE} || return 1; 
	(( ZONE_EXISTS++ ));
	return 0;
}

RNDC_SHOW_ZONE()
{
	LOG "${AD_DOMAIN,,}"
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key showzone ${AD_DOMAIN,,} || return 1;
	(( ZONE_EXISTS++ ));
	return 0;
}

RNDC_RELOAD()
{
	LOG "${AD_DOMAIN,,}"
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key nta -lifetime 168h ${AD_DOMAIN,,} ;
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key reload ${AD_DOMAIN,,} || return 1;
	sleep 6s;
	return 0;
}

RNDC_ZONE_STATUS()
{
	LOG "${AD_DOMAIN,,}"
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key zonestatus ${AD_DOMAIN,,} || return 1;
	(( ZONE_EXISTS++ ));
	return 0;
}

RNDC_FLUSH_ZONE()
{
	LOG "${AD_DOMAIN,,}"
	[[ ZONE_EXISTS -gt 1 ]] || return 0;
	/usr/sbin/rndc -k ${RNDC_KEY} -y rndc-key flushtree ${AD_DOMAIN,,} && return 0;
	return 1;
}


CHECK_DNS_CONFIG()
{
	LOG "${AD_FQDN,,}"
	local i;
	let i=0;
	let za=0; 
	local ZONE="${AD_FQDN,,}";
	for ((i=0; i < 3; i++))
	do
		host -t A ${AD_FQDN,,} && return 0;
		RNDC_SHOW_ZONE
		RNDC_DEL_ZONE 
		RNDC_ADD_ZONE
		RNDC_FLUSH_ZONE
		RNDC_RELOAD;
		RNDC_ZONE_STATUS;		
	done
	return 1;
}


DEPENDENCY_CHECK()
{
	local PAKG=""

	#msktutil dependency-check.
	LOG "checking usability of msktutil"
	[ ! -f "${_MSKTUTIL}" ] && LOG "library: ${_MSKTUTIL}: not found" && return 1;
    PAKG=$( ldd ${_MSKTUTIL} | grep "not found" )
    [ "x${PAKG}" != "x" ] && LOG "library:${PAKG}" && return 1;
	
	return 0;
}

KINIT_USER()
{		
	RET=1
	_AD_USER="${LDAP_USER}@${AD_DOMAIN^^}"
	LOG "kinit user: _AD_USER: ${_AD_USER}"
	echo ${LDAP_PASSWORD} | ${_KINIT} --password-file=STDIN  ${_AD_USER} && LOG "kinit: successful" && return 0;
	LOG "${_KINIT}: failed" 
	return 1;
}

KEYTAB_CLEANUP()
{
	local T="$1"
	[ "x$T" == "x" ] && return 0;
	LOG "Cleaning ${T}"	
	
	${_KTUTIL} -k "$T" purge --age=0
	
#	${_MSKTUTIL} cleanup --remove-old 1 --keytab "$T"
	return $?
}

KEYTAB_CREATE()
{
	local RET=0
	[ -f ${KRB5_KTNAME} ] && LOG "exists: ${KRB5_KTNAME}" && return 0;

	LOG "NOT exists: ${KRB5_KTNAME}"
	LOG "creating: ${KRB5_KTNAME}"

	declare -a OPTS
	export MSKTUTIL_KEYTAB=${KRB5_KTNAME}

	
	OPTS=();
	OPTS+=("--create")
	[[ "x${VERBOSE}" != "x" ]] && OPTS+=(--verbose)
	OPTS+=(--base 'CN=COMPUTERS')
	OPTS+=(--keytab ${KRB5_KTNAME})
	OPTS+=(--upn ${SPN}.${REALM})
	OPTS+=(--computer-name ${SPN})
	OPTS+=(--hostname ${SPN}.${REALM})
	OPTS+=(--server ${AD_FQDN})
	OPTS+=(--no-reverse-lookups)
	OPTS+=(--realm ${REALM})
	OPTS+=(--dont-expire-password)
	OPTS+=(--no-canonical-name)
	OPTS+=(--no-pac)

	LOG "${_MSKTUTIL} ${OPTS[*]}"
	OPTS+=(--password ${LDAP_PASSWORD})
	${_MSKTUTIL} ${OPTS[*]} && return 0;
	
	return 1;	
}

KEYTAB_UPDATE()
{
	local RET=1
	declare -a OPTS
	OPTS=();
	
	OPTS+=("--update")
	
	LOG "updating: ${KRB5_KTNAME}"
	
	[[ "x${VERBOSE}" != "x" ]] && OPTS+=(--verbose)
	OPTS+=(--base 'CN=COMPUTERS')
	OPTS+=(--service HTTP/${SPN}.${REALM})
	OPTS+=(--service LDAP/${SPN}.${REALM})
	OPTS+=(--service HOST/${SPN}.${REALM})
	OPTS+=(--service HTTP/${COMPUTER_NAME}.${REALM})
	OPTS+=(--service LDAP/${COMPUTER_NAME}.${REALM})
	OPTS+=(--service HOST/${COMPUTER_NAME}.${REALM})

	OPTS+=(--keytab ${KRB5_KTNAME})
	OPTS+=(--upn ${SPN}.${REALM})
	OPTS+=(--computer-name ${SPN})
	OPTS+=(--hostname ${SPN}.${REALM})
	OPTS+=(--server ${AD_FQDN})
	OPTS+=(--user-creds-only)
	OPTS+=(--no-reverse-lookups)
	OPTS+=(--dont-expire-password)
	OPTS+=(--no-canonical-name)
	OPTS+=(--realm ${REALM})

	
	LOG "${_MSKTUTIL} ${OPTS[*]}"
	OPTS+=(--password ${LDAP_PASSWORD})
	${_MSKTUTIL} ${OPTS[*]} && return 0;
	
	return 1;
}


KEYTAB_MERGE ()
{
	[ ! -f ${KRB5_KTNAME} ] && LOG "${KRB5_KTNAME}: not found"
	let D=$(( 0 ))
	let N=$(( 0 ))
	
	[ -f ${KRB5_KTNAME} ] && KEYTAB_CLEANUP "${KRB5_KTNAME}"
	[ -f ${HTTP_KEYTAB} ] && KEYTAB_CLEANUP "${HTTP_KEYTAB}"
	
	[ -f ${HTTP_KEYTAB} ] && D=`date -u -r ${HTTP_KEYTAB} +"%s"`
	
	[ -f ${KRB5_KTNAME} ] && ${_KTUTIL} -k "${KRB5_KTNAME}" list --keys --timestamp 
			
	LOG "Merging ${KRB5_KTNAME} -> ${HTTP_KEYTAB}"	
	${_KTUTIL} copy ${KRB5_KTNAME} ${HTTP_KEYTAB} 

	[ -f ${HTTP_KEYTAB} ] && ${_KTUTIL} -k "${HTTP_KEYTAB}" list --keys --timestamp 


	[ -f ${HTTP_KEYTAB} ] && N=`date -u -r ${HTTP_KEYTAB} +"%s"`
	
	[[ D -ne N ]] && return 0;
	
	LOG "D: $D N: $N"

	LOG "failed"
	return 1;
}

PURGE_OLD_KEYS()
{
	return 0;
	LOG "Purging old or duplicate keys"
	${_KTUTIL} -k ${HTTP_KEYTAB} purge --age=0
	${_KTUTIL} -k ${HTTP_KEYTAB} list --keys --timestamp 
	
	${_KTUTIL} -k ${KRB5_KTNAME} purge --age=0
	${_KTUTIL} -k ${KRB5_KTNAME} list --keys --timestamp 
}

MAIN()
{	
	local RET=0
	LOG "\n#####  `date` kerberos setup ########\n"

	CHECK_PARAMETERS || return 1;

	MAKE_REALM_CONF	
	MAKE_KRB_CONF
	
	CHECK_DNS_VALIDITY "${AD_FQDN,,}" "${AD_IP}" || return 1;
	CHECK_DNS_CONFIG || return 1;
	
	HTTP_KEYTAB=${KRB5_KTNAME}		
	DEPENDENCY_CHECK || return 1;


	export KRB5_KTNAME=${HTTP_KEYTAB}.${REALM}

	KINIT_USER || return 1;
	
	KEYTAB_CREATE || return 1;

	KEYTAB_UPDATE || return 1;

	KEYTAB_MERGE || return 1;

	export KRB5_KTNAME=${HTTP_KEYTAB}
		
	PURGE_OLD_KEYS
	
	LOG "Generate Keytab: successful"
	
	CHECK_DNS_VALIDITY ${MY_COMPUTER_NAME}.${AD_DOMAIN,,} ${AD_IP}
 	LOG "Successful Task End"
	return 0;
}

MAIN && exit 0
LOG "Generate Keytab: failed"
exit 1

:<<-_EOF
	MSKTUTIL Environment Variables 
	
	MSKTUTIL_LDAP_BASE
		Specifies a relative LDAP base when creating a new account (see --base),

	MSKTUTIL_KEYTAB
		Specifies the keytab. Default: /etc/krb5.keytab (see --keytab),

	MSKTUTIL_SERVER
		Specifies the domain controller (see --server).

	MSKTUTIL_DELEGATION
		Enables the account to be trusted for delegation (see --delegation).

	MSKTUTIL_NO_PAC
		Specifies that service tickets for this account should not contain a PAC (see --no-pac).
_EOF

:<<- _EOF
	Several  environment  variables  affect the operation of Kerberos-enabled programs. 
	These include:

	KRB5CCNAME
		Default name for the credentials cache file, in the form TYPE:residual.   The  type
		of the default cache may determine the availability of a cache collection.  FILE is
		not a collection type; KEYRING, DIR, and KCM are.

		If not  set,  the  value  of  default_ccache_name  from  configuration  files  (see
		KRB5_CONFIG)  will be used.  If that is also not set, the default type is FILE, and
		the residual is the path /tmp/krb5cc_*uid*, where uid is the decimal user ID of the
		user.

	KRB5_KTNAME
		Specifies  the  location of the default keytab file, in the form TYPE:residual.  If
		no type is present, the FILE type is assumed and residual is the  pathname  of  the
		keytab file.  If unset, FILE:/etc/krb5.keytab will be used.

	KRB5_CONFIG
		Specifies  the  location  of  the  Kerberos  configuration  file.   The  default is
		/etc/krb5.conf.  Multiple filenames can be specified, separated  by  a  colon;  all
		files which are present will be read.

	KRB5_KDC_PROFILE
		Specifies  the  location  of  the KDC configuration file, which contains additional
		configuration directives for the Key  Distribution  Center  daemon  and  associated
		programs.  The default is /etc/krb5kdc/kdc.conf.

	KRB5RCACHETYPE
		Specifies the default type of replay cache to use for servers.  Valid types include
		dfl for the normal file type and none for no replay cache.  The default is dfl.

	KRB5RCACHEDIR
		Specifies the default directory for replay caches used by servers.  The default  is
		the value of the TMPDIR environment variable, or /var/tmp if TMPDIR is not set.

	KRB5_TRACE
		Specifies  a filename to write trace log output to.  Trace logs can help illuminate
		decisions  made  internally  by  the  Kerberos   libraries.    For   example,   env
		KRB5_TRACE=/dev/stderr  kinit  would  send  tracing  information  for  kinit(1)  to
		/dev/stderr.  The default is not to write trace log output anywhere.

	KRB5_CLIENT_KTNAME
		Default       client       keytab        file        name.         If        unset,
		FILE:/etc/krb5/user/%{euid}/client.keytab will be used).

	KPROP_PORT
		kprop(8) port to use.  Defaults to 754.

	Most  environment  variables  are  disabled  for  certain  programs,  such as login system
	programs and setuid programs, which are designed to be secure when run within an untrusted
	process environment.

_EOF
