Current File : //sbin/kclient
#!/bin/ksh93 -p
#
# Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This script is used to setup the Kerberos client by
# supplying information about the Kerberos realm and kdc.
#
# The kerberos configuration file (/etc/krb5/krb5.conf) would
# be generated and local host's keytab file setup. The script
# can also optionally setup the system to do kerberized nfs and
# bringover a master krb5.conf copy from a specified location.
#

function cleanup {

	kdestroy -q > $TMP_FILE 2>&1
	rm -r $TMPDIR > /dev/null 2>&1

	exit $1
}
function exiting {
	
        printf "\n$(gettext "Exiting setup, nothing changed").\n\n"

	cleanup $1
}

function error_message {

        printf -- "---------------------------------------------------\n" >&2
        printf "$(gettext "Setup FAILED").\n\n" >&2

        cleanup 1
}

function check_bin {

	typeset bin=$1

	if [[ ! -x $bin ]]; then
		printf "$(gettext "Could not access/execute %s").\n" $bin >&2
			if [[ $bin == $KSMB || $bin == $KDYNDNS ]]; then
				printf "$(gettext "Warning: unable to detect the SMB client package").\n"
				printf "$(gettext "Please install the package for support in SMB environments").\n"
				return
			fi
		error_message
	fi
}

function cannot_create {
	typeset filename="$1"
	typeset stat="$2"

	if [[ $stat -ne 0 ]]; then
		printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2
		error_message
	fi
}

function update_pam_conf {
	typeset PAM_CONF PAM_D_CONF TPAM_D_CONF POLICY_CONF TPOLICY_CONF service
	typeset INC_FILE MOD

	PAM_CONF=/etc/pam.conf
	PAM_D_CONF=/etc/pam.d
	POLICY_CONF=/etc/security/policy.conf

	TPAM_D_CONF=$(mktemp -q -t kclient-pam.d-conf.XXXXXX)
	if [[ -z $TPAM_D_CONF ]]; then
		printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2
		error_message
	fi

	printf "$(gettext "Configuring PAM").\n\n"

	for service in $SVCs; do
		svc=${service%:*}
		auth_type=${service#*:}

		if [[ -n ${service/*:*/} ]]; then
			# Update PAM_POLICY with auth_type
			if grep "^[	 ]*PAM_POLICY" $POLICY_CONF >/dev/null \
			    2>&1; then
				printf "$(gettext "PAM_POLICY is already configured, please merge %s by \nhand with"):\n\n" \
				    $POLICY_CONF
				printf "PAM_POLICY=krb5_$auth_type\n\n"
				continue
			fi
			TPOLICY_CONF=$(mktemp -q -t kclient-policy-conf.XXXXXX)
			if [[ -z $TPOLICY_CONF ]]; then
				printf "\n$(gettext "Can not create temporary file, exiting: %s").\n" $TPOLICY_CONF >&2
				error_message
			fi
			sed "s%^#PAM_POLICY=%PAM_POLICY=krb5_$auth_type%" \
			    $POLICY_CONF >$TPOLICY_CONF
			cp -f $TPOLICY_CONF $POLICY_CONF >/dev/null 2>&1
			rm $TPOLICY_CONF > /dev/null 2>&1
			continue
		fi
		# If cron has been selected for configuration then this is most
		# likely a S4U configuration else we configure the appropriate
		# pam_krb5 auth type.
		if [[ $svc == cron ]]; then
			MOD=pam_gss
			INC_FILE=${MOD}_s4u
		else
			MOD=pam_krb5
			INC_FILE=${MOD}_${auth_type}
		fi
		if grep "^[	 ]*$svc[	 ]\{1,\}auth.*$MOD*" \
		    $PAM_CONF >/dev/null 2>&1; then
			printf "$(gettext "The %s service is already configured for %s, please merge the\nfollowing entry into %s by hand"):\n\n" \
			    $svc $MOD $PAM_CONF >&2
			printf "$svc\tauth\tinclude\t\t$INC_FILE\n\n"
			continue
		fi
		if grep "^[ 	]*$svc[ 	]\{1,\}auth[ 	]\{1,\}" \
		    $PAM_CONF >/dev/null 2>&1; then
			printf "$(gettext "The %s service is already configured for authentication in\n%s, please merge the following entry into %s by hand"):\n\n" $svc $PAM_CONF $PAM_CONF >&2
			printf "$svc\tauth\tinclude\t\t$INC_FILE\n\n"
			continue
		else if grep "^[ 	]*auth[ 	]\{1,\}" \
		    $PAM_D_CONF/$svc >/dev/null 2>&1; then
			printf "$(gettext "The %s service is already configured for authentication in\n%s, please merge the following entry into\n%s by hand"):\n\n" $svc $PAM_D_CONF/$svc $PAM_D_CONF/$svc >&2
			printf "auth\tinclude\t\t$INC_FILE\n\n"
			continue
		    fi
		fi
		# update /etc/pam.d/$svc since this $svc isn't already
		# configured for authentication in /etc/pam.conf or
		# /etc/pam.d/$svc
		printf "$(gettext "Updating /etc/pam.d/$svc").\n"
		cp -f $PAM_D_CONF/$svc $TPAM_D_CONF >/dev/null 2>&1
		exec 3>>$TPAM_D_CONF
		printf "\nauth include\t\t$INC_FILE\n" 1>&3
		cp -f $TPAM_D_CONF $PAM_D_CONF/$svc > /dev/null 2>&1
	done

	rm $TPAM_D_CONF > /dev/null 2>&1
}

function modify_nfssec_conf {
	typeset NFSSEC_FILE="/etc/nfssec.conf"

	if [[ -r $NFSSEC_FILE ]]; then
		cat $NFSSEC_FILE > $NFSSEC_FILE.sav
		cannot_create $NFSSEC_FILE.sav $?
	fi

	cat $NFSSEC_FILE > $TMP_FILE
	cannot_create $TMP_FILE $?

	if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1; then
		sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE
		cannot_create $NFSSEC_FILE $?
	fi
}

function write_xml_profile {
	typeset output_file="$1"
	typeset cmd=""
	typeset KRB_INST_SVC="system/kerberos/install"
	typeset PROP=" -s instance-property="

	#
	# Construct svcbundle command to create the configuration profile.
	#
	cmd=$cmd"svcbundle -o $output_file -s bundle-type=profile "
	cmd=$cmd" -s enabled=true -s model=transient -s instance-name=default "
	cmd=$cmd" -s service-name=$KRB_INST_SVC"
	cmd=$cmd$PROP"install:config_complete:boolean:false"
	cmd=$cmd$PROP"install:major_version:integer:0"
	cmd=$cmd$PROP"install:minor_version:integer:1"
	
	if [[ "$no_keytab" == 'yes' ]]; then
		cmd=$cmd$PROP"libdefaults:verify_ap_req_nofail:boolean:false"
	fi
	cmd=$cmd$PROP"libdefaults:default_realm:astring:$realm"
	if [[ $dns_lookup == yes ]]; then
	    cmd=$cmd$PROP"libdefaults:$dnsarg:boolean:true"
	    if [[ $dnsarg == dns_lookup_kdc ]]; then
		if [[ -n $domain_list ]]; then
			for dh in $domain_list; do
				cmd=$cmd$PROP"default_realm:domains:astring:$dh"
			done
		fi
	    elif [[ $dnsarg = dns_lookup_realm ]]; then
		if [[ -n $slave_kdc_list ]]; then
			for kdc in $slave_kdc_list; do
				cmd=$cmd$PROP"default_realm:kdc:hostname:$kdc"
			done
		fi
		for kdc in $master_kdc_list; do
			cmd=$cmd$PROP"default_realm:kdc:hostname:$kdc"
		done
		for kdc in $master_kdc_list; do
			cmd=$cmd$PROP"default_realm:admin_server:hostname:$kdc"
		done
		if [[ $non_solaris == yes ]]; then
			cmd=$cmd$PROP"default_realm:kpasswd_protocol:"
			cmd=$cmd"astring:SET_CHANGE"
		fi
	    fi
	else
	    if [[ -n $slave_kdc_list ]]; then
		for kdc in $slave_kdc_list; do
			cmd=$cmd$PROP"default_realm:kdc:hostname:$kdc"
		done
	    fi
	    for kdc in $master_kdc_list; do
		    cmd=$cmd$PROP"default_realm:kdc:hostname:$kdc"
	    done
	    for kdc in $master_kdc_list; do
		    cmd=$cmd$PROP"default_realm:admin_server:hostname:$kdc"
	    done
	    if [[ $non_solaris == yes ]]; then
		cmd=$cmd$PROP"default_realm:kpasswd_protocol:astring:SET_CHANGE"
	    fi

	    if [[ -n $domain_list ]]; then
		for dh in $domain_list; do
			cmd=$cmd$PROP"default_realm:domains:astring:$dh"
		done
	    fi
	fi

	cmd=$cmd$PROP"appdefaults-kinit:renewable:boolean:true"
	cmd=$cmd$PROP"appdefaults-kinit:forwardable:boolean:true"
	if [[ "$no_keytab" == 'yes' ]]; then
		cmd=$cmd$PROP"appdefaults-kinit:no_addresses:boolean:true"
	fi

	if [[ -n $b64_keytab ]]; then
		cmd=$cmd$PROP"autoreg:princ:astring:$ADMIN_PRINC"
		cmd=$cmd$PROP"autoreg:key:astring:$b64_keytab"
		if [[ -n $logical_hn ]]; then
			cmd=$cmd$PROP"autoreg:lhn:astring:$logical_hn"
		fi
		for fqdn in $fqdnlist; do
			cmd=$cmd$PROP"autoreg:fqdns:astring:$fqdn"
		done
		cmd=$cmd$PROP"autoreg:services:astring:host"
		if [[ $add_nfs == 'yes' ]]; then
			cmd=$cmd$PROP"autoreg:services:astring:nfs"
		fi
		cmd=$cmd$PROP"autoreg:kdctype:astring:$kdctype"
	fi

	# call the command and return its error code
	printf "\n$(gettext "Setting up %s").\n\n" $output_file
	exec $cmd
}

function xml_profile_prompts {
	#
	# Check to see if we will be a client of a MIT, Heimdal, Shishi, etc.
	#
	if [[ -z $options ]]; then
		query "$(gettext "Is this a client of a non-Solaris KDC") ?"
		non_solaris=$answer
		if [[ $non_solaris == yes ]]; then
			printf "$(gettext "Which type of KDC is the server"):\n"
			printf "\t$(gettext "ms_ad: Microsoft Active Directory")\n"
			printf "\t$(gettext "mit: MIT KDC server")\n"
			printf "\t$(gettext "heimdal: Heimdal KDC server")\n"
			printf "\t$(gettext "shishi: Shishi KDC server")\n"
			printf "$(gettext "Enter required KDC type"): "
			read kdctype
			if [[ $kdctype == ms_ad ]]; then
				msad=yes
			elif [[ $kdctype == mit || $kdctype == heimdal || \
			    $kdctype == shishi ]]; then
				no_keytab=yes
			else
				printf "\n$(gettext "Invalid KDC type option, valid types are ms_ad, mit, heimdal, or shishi, exiting").\n" >&2
				error_message
			fi
		fi
	fi
	[[ -z $kdctype ]] && kdctype='solaris'

	# Auto-registration to MS AD domain
	if [[ $kdctype == 'ms_ad' ]]; then
		query "$(gettext "Should the client automatically join AD domain") ?"
		if [[ $answer == 'yes' ]]; then
			if [[ -z $realm ]]; then
				printf "$(gettext "Enter the Kerberos realm"): "
				read realm
				checkval="REALM"; check_value $realm
			fi
			if [[ -z $ADMIN_PRINC ]]; then
				printf "\n$(gettext "Enter the krb5 administrative principal to be used"): "
				read ADMIN_PRINC
				checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
			fi
			printf "\n$(gettext "Password for %s"): " $ADMIN_PRINC
			stty -echo
			read -s pw
			stty echo
			echo
			b64_keytab=$(create_b64_keytab $ADMIN_PRINC $pw $realm)
			return
		fi
	fi

	check_nss_conf || printf "$(gettext "/etc/nsswitch.conf does not make use of DNS for hosts and/or ipnodes").\n"

	if [[ -z $dnsarg && (-z $options || -z $filepath) ]]; then
		query "$(gettext "Do you want to use DNS for kerberos lookups") ?"
		if [[ $answer == yes ]]; then
			printf "\n$(gettext "Valid DNS lookup options are dns_lookup_kdc, dns_lookup_realm,\nand dns_fallback. Refer krb5.conf(4) for further details").\n"
			printf "\n$(gettext "Enter required DNS option"): "
			read dnsarg
			checkval="DNS_OPTIONS"; check_value $dnsarg
			set_dns_value $dnsarg
		fi
	fi
	[[ -z $dnsarg ]] && dnsarg=none
	set_dns_value $dnsarg

	if [[ -n $kdc_list ]]; then
		if [[ -z $master_kdc_list ]]; then
			# assign first kdc to master kdc list, rest to slave list
			print $kdc_list|read master_kdc_list slave_kdc_list
		else
			# masters were already specified so this is just slaves
			slave_kdc_list="$kdc_list"
		fi
	fi

	if [[ -z $realm ]]; then
		printf "$(gettext "Enter the Kerberos realm"): "
		read realm
		checkval="REALM"; check_value $realm
	fi

	if [[ $dnsarg == 'none' || $dnsarg == 'dns_lookup_realm' ]]; then
		if [[ -z $master_kdc_list ]]; then
			printf "$(gettext "Specify the master KDCs for the above realm using a comma-separated list"): "
			read master_kdc_list
		fi
		verify_kdcs $master_kdc_list 'no_ping_check'
		master_kdc_list=${master_kdc_list//,/ }

		if [[ -z $slave_kdc_list && -z $options ]]; then
			query "$(gettext "Do you have any slave KDC(s)") ?"
			if [[ $answer == yes ]]; then
				printf "$(gettext "Enter a comma-separated list of slave KDC host names"): "
				read slave_kdc_list
			fi
		fi
		[[ -n $slave_kdc_list ]] && verify_kdcs $slave_kdc_list 'no_ping_check'
		slave_kdc_list=${slave_kdc_list//,/ }

		found=false
		for kdc in $slave_kdc_list $master_kdc_list; do
			if ping $kdc 2 > /dev/null; then
				found=true
				break
			fi
		done
		! $found && printf "\n$(gettext "Warning, no %s is reachable.").\n\n" "KDC" >&2
	fi

	if [[ $dnsarg == 'none' || $dnsarg == 'dns_lookup_kdc' ]]; then
		if [[ -z $domain_list && (-z $options || -z $filepath) ]]; then
			query "$(gettext "Do you have multiple domains/hosts to map to realm %s"
		) ?" $realm
			if [[ $answer == yes ]]; then
				printf "$(gettext "Enter a comma-separated list of domain/hosts
		to map to the default realm"): "
				read domain_list
			fi
		fi
		[[ -n $domain_list ]] && domain_list=${domain_list//,/ }
	fi

	# autoregistration not available for mit_kdc, shishi, heimdal.
	if [[ $kdctype != 'solaris' || $no_keytab = 'yes' ]]; then
		return
	fi

	query "$(gettext "Should the client automatically join the realm") ?"
	if [[ $answer == 'yes' ]]; then
		if [[ -z $ADMIN_PRINC ]]; then
			printf "\n$(gettext "Enter the krb5 administrative principal to be used"): "
			read ADMIN_PRINC
			checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
		fi
		printf "\n$(gettext "Password for %s"): " $ADMIN_PRINC
		stty -echo
		read -s pw
		stty echo
		echo
		b64_keytab=$(create_b64_keytab $ADMIN_PRINC $pw $realm)

		# Is this client going to use krb-nfs? 
		if [[ -z $options ]]; then
			query "$(gettext "Do you plan on doing Kerberized nfs") ?"
			add_nfs=$answer
		fi

		# Cluster with logical hostname - different service principals
		if [[ -z $options ]]; then
			query "$(gettext "Is this client a member of a cluster that uses a logical host name") ?"
			if [[ $answer == yes ]]; then
				printf "$(gettext "Specify the logical hostname of the cluster"): "
				read logical_hn
				checkval="LOGICAL_HOSTNAME"; check_value $logical_hn
			fi
		fi

		# Create keys for service principals in multiple domains?
		if [[ -z $options ]]; then
			query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?"
			if [[ $answer == yes ]]; then
				printf "$(gettext "Enter a comma-separated list of DNS domain names"): "
				read fqdnlist
			fi
		fi
		[[ -n $fqdnlist ]] && fqdnlist=${fqdnlist//,/ }
	fi
}

#
# Given principal name and password, creates a keytab 
# and prints it base64 encoded.
#
function create_b64_keytab {
	typeset princ=$1
	typeset pass=$2
	typeset realm=$3
	typeset args rtrn

	# ksetpw requires some krb profile to work, any profile
	echo "[libdefaults]\ndefault_realm = $realm" > $KRB5_CONFIG

	args=" -n -s $princ -v2 -k $new_keytab "
	args=$args" -e aes256-cts-hmac-sha1-96 -e aes128-cts-hmac-sha1-96 "
	args=$args" -e des3-cbc-sha1 -e rc4-hmac "
	rm $new_keytab > /dev/null 2>&1
	echo $pass | $KSETPW $args $princ@$realm >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		printf "\n$(gettext "Failed to create admin keytab.").\n" >&2
		error_message
	fi
	rtrn=$(openssl base64 -in $new_keytab | tr -d '\n')
	if [[ $? -ne 0 ]]; then
		printf "\n$(gettext "Failed to encode admin keytab.").\n" >&2
		error_message
	fi
	rm $new_keytab > /dev/null 2>&1
	print -- $rtrn
}

function call_kadmin {
	typeset svc="$1"
	typeset bool1 bool2 bool3 bool4
	typeset service_princ getprincsubcommand anksubcommand ktaddsubcommand
	typeset ktremsubcommand

	for listentry in $fqdnlist; do

	# Reset conditional vars to 1
	bool1=1; bool2=1; bool3=1; bool4=1

	service_princ=$(echo "${svc}/${listentry}")
	getprincsubcommand="getprinc $service_princ"
	anksubcommand="addprinc -randkey $service_princ"
	ktaddsubcommand="ktadd $service_princ"
	ktremsubcommand="ktrem $service_princ all"

	kadmin -c $KRB5CCNAME -q "$getprincsubcommand" 1>$TMP_FILE 2>&1

	egrep -s "$(gettext "get_principal: Principal does not exist")" $TMP_FILE
	bool1=$?
	egrep -s "$(gettext "get_principal: Operation requires ``get")" $TMP_FILE
	bool2=$?

	if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then
		kadmin -c $KRB5CCNAME -q "$anksubcommand" 1>$TMP_FILE 2>&1

		egrep -s "$(gettext "add_principal: Principal or policy already exists while creating \"$service_princ@$realm\".")" $TMP_FILE
		bool3=$?

		egrep -s "$(gettext "Principal \"$service_princ@$realm\" created.")" $TMP_FILE
		bool4=$?

		if [[ $bool3 -eq 0 || $bool4 -eq 0 ]]; then
			printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ
		else
			cat $TMP_FILE;
			printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ >&2
			error_message
		fi
	else
		printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ >&2
	fi

	klist -k 1>$TMP_FILE 2>&1
	egrep -s "$service_princ@$realm" $TMP_FILE
	if [[ $? -eq 0 ]]; then
		printf "$(gettext "%s entry already present in keytab").\n" $service_princ >&2
		# Don't care is this succeeds or not, just need to replace old
		# entries as it is assummed that the client is reinitialized
		kadmin -c $KRB5CCNAME -q "$ktremsubcommand" 1>$TMP_FILE 2>&1
	fi

	kadmin -c $KRB5CCNAME -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1
	egrep -s "$(gettext "added to keytab WRFILE:$KRB5_KEYTAB_FILE.")" $TMP_FILE
	if [[ $? -ne 0 ]]; then
		cat $TMP_FILE;
		printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ >&2
		error_message
	else
		printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ
	fi

	done
}

function writeup_krb5_conf {
	typeset dh

	printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE

	exec 3>$KRB5_CONFIG
	if [[ $? -ne 0 ]]; then
		printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2
		error_message
	fi

	printf "[libdefaults]\n" 1>&3
	if [[ $no_keytab == yes ]]; then
		printf "\tverify_ap_req_nofail = false\n" 1>&3
	fi
	if [[ $dns_lookup == yes ]]; then
	    printf "\t$dnsarg = on\n" 1>&3
	    if [[ $dnsarg == dns_lookup_kdc ]]; then
		printf "\tdefault_realm = $realm\n" 1>&3
		printf "\n[domain_realm]\n" 1>&3
		printf "\t$client_machine = $realm\n" 1>&3
		if [[ -z $short_fqdn ]]; then
			printf "\t.$domain = $realm\n\n" 1>&3
		else
			printf "\t.$short_fqdn = $realm\n\n" 1>&3
		fi
		if [[ -n $domain_list ]]; then
			for dh in $domain_list; do
				printf "\t$dh = $realm\n" 1>&3
			done
		fi
	    else
		if [[ $dnsarg = dns_lookup_realm ]]; then
		    printf "\tdefault_realm = $realm\n" 1>&3
		    printf "\n[realms]\n" 1>&3
		    printf "\t$realm = {\n" 1>&3
		    if [[ -n $slave_kdc_list ]]; then
			for kdc in $slave_kdc_list; do
				printf "\t\tkdc = $kdc\n" 1>&3
			done
		    fi
		    for kdc in $master_kdc_list; do
			printf "\t\tkdc = $kdc\n" 1>&3
		    done
		    for kdc in $master_kdc_list; do
			    printf "\t\tadmin_server = $kdc\n" 1>&3
		    done
		    if [[ $non_solaris == yes ]]; then
			printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3
		    fi
		    printf "\t}\n\n" 1>&3
		else
		    printf "\tdefault_realm = $realm\n\n" 1>&3
		fi
	    fi
	else
	    printf "\tdefault_realm = $realm\n\n" 1>&3

	    printf "[realms]\n" 1>&3
	    printf "\t$realm = {\n" 1>&3
	    if [[ -n $slave_kdc_list ]]; then
		for kdc in $slave_kdc_list; do
			printf "\t\tkdc = $kdc\n" 1>&3
		done
	    fi
	    for kdc in $master_kdc_list; do
		    printf "\t\tkdc = $kdc\n" 1>&3
	    done
	    for kdc in $master_kdc_list; do
		    printf "\t\tadmin_server = $kdc\n" 1>&3
	    done
	    if [[ $non_solaris == yes ]]; then
	    	printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3
	    fi
	    printf "\t}\n\n" 1>&3

	    printf "[domain_realm]\n" 1>&3
	    printf "\t$client_machine = $realm\n" 1>&3
	    if [[ -z $short_fqdn ]]; then
		printf "\t.$domain = $realm\n\n" 1>&3
	    else
		printf "\t.$short_fqdn = $realm\n\n" 1>&3
	    fi
	    if [[ -n $domain_list ]]; then
		for dh in $domain_list; do
			printf "\t$dh = $realm\n" 1>&3
		done
	    fi
	fi

	printf "[logging]\n" 1>&3
	printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3
	printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3
	printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3

	printf "[appdefaults]\n" 1>&3
	printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n" 1>&3
	if [[ $no_keytab == yes ]]; then
		printf "\t\tno_addresses = true\n" 1>&3
	fi
	printf "\t}\n" 1>&3
}

function ask {
	typeset question=$1
	typeset default_answer=$2

	if [[ -z $default_answer ]]; then
		printf "$question :"
	else
		printf "$question [$default_answer]: "
	fi
	read answer
	test -z "$answer" && answer="$default_answer"
}

function yesno {
	typeset question="$1"

	answer=
	yn=`printf "$(gettext "y/n")"`
	y=`printf "$(gettext "y")"`
	n=`printf "$(gettext "n")"`
	yes=`printf "$(gettext "yes")"`
	no=`printf "$(gettext "no")"`

	while [[ -z $answer ]]; do
		ask "$question" $yn
		case $answer in
			$y|$yes)	answer=yes;;
			$n|$no)		answer=no;;
			*)		answer=;;
		esac
	done
}

function query {
	yesno "$*"

	if [[ $answer == no ]]; then
		printf "\t$(gettext "No action performed").\n"
	fi
}


function read_profile {
	typeset param value
	typeset file="$1"

	if [[ ! -d $file && -r $file ]]; then
		while read param value
		do
			case $param in
			REALM)  if [[ -z $realm ]]; then
					realm="$value"
					checkval="REALM"; check_value $realm
				fi
				;;
			MAS)    if [[ -z $master_kdc_list ]]; then
					master_kdc_list="$value"
				fi
				;;
			KDC)    if [[ -z $kdc_list ]]; then
					kdc_list="$value"
				fi
				;;
			ADMIN)  if [[ -z $ADMIN_PRINC ]]; then
					ADMIN_PRINC="$value"
					checkval="ADMIN_PRINC"
    					check_value $ADMIN_PRINC
				fi
				;;
			FILEPATH)  if [[ -z $filepath ]]; then
					filepath="$value"
				   fi
				   ;;
			NFS)    if [[ -z $add_nfs ]]; then
				    if [[ $value == 1 ]]; then
					    add_nfs=yes
				    else
					    add_nfs=no
				    fi
				fi
				;;
			NOKEY)    if [[ -z $no_keytab ]]; then
				    if [[ $value == 1 ]]; then
					    no_keytab=yes
				    else
					    no_keytab=no
				    fi
				fi
				;;
			NOSOL)  if [[ -z $non_solaris ]]; then
				    if [[ $value == 1 ]]; then
					    non_solaris=yes
					    no_keytab=yes
				    else
					    non_solaris=no
				    fi
				fi
				;;
			LHN)    if [[ -z $logical_hn ]]; then
					logical_hn="$value"
					checkval="LOGICAL_HOSTNAME"
    					check_value $logical_hn
				fi
				;;
			DNSLOOKUP) if [[ -z $dnsarg ]]; then
					dnsarg="$value"
					checkval="DNS_OPTIONS"
					check_value $dnsarg
				   fi
				   ;;
			FQDN) if [[ -z $fqdnlist ]]; then
					fqdnlist="$value"
					checkval="FQDN"
					check_value $fqdnlist
			      fi
			      ;;
			MSAD) if [[ -z $msad ]]; then
				if [[ $value == 1 ]]; then
					msad=yes
					non_solaris=yes
				else
					msad=no
				fi
			      fi
			      ;;
			KDCVENDOR) if [[ -z $kdctype ]]; then
				      kdctype="$value"
				   fi
			           if [[ $kdctype == ms_ad ]]; then
				     msad=yes
				     adddns=yes
			           else
				     non_solaris=yes
				     no_keytab=yes
			           fi
				   ;;
			RMAP)	if [[ -z $domain_list ]]; then
					domain_list="$value"
				fi
				;;
			PAM)    if [[ -z $svc_list ]]; then
					svc_list="$value"
					verify_pam_svc_list $svc_list
					SVCs=${svc_list//,/ }
				fi
				;;
			esac
		done <$file
	else
		printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file >&2
		error_message
	fi
}

function ping_check {
	typeset machine="$1"
	typeset string="$2"

	if ping $machine 2 > /dev/null 2>&1; then
		:
	else
		printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine >&2
		error_message
	fi

	# Output timesync warning if not using a profile, i.e. in
	# interactive mode.
	if [[ -z $profile && $string == KDC ]]; then
		# It's difficult to sync up time with KDC esp. if in a
		# zone so just print a warning about KDC time sync.
		printf "\n$(gettext "Note, this system and the KDC's time must be within 5 minutes of each other for Kerberos to function").\n" >&2
		printf "$(gettext "Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n" >&2
break
	fi
}

function check_value {
	typeset arg="$1"

	if [[ -z $arg ]]; then
		printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval >&2
		error_message
	else
		echo "$arg" > $TMP_FILE
		if egrep -s '[*$^#!]+' $TMP_FILE; then
			printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval >&2
			error_message
		fi
	fi
}

function set_dns_value {
	typeset -l arg="$1"

	if [[ $arg == dns_lookup_kdc  ||  $arg == dns_lookup_realm  || $arg == dns_fallback ]]; then
		dns_lookup=yes
	else
		if [[ $arg == none ]]; then
			dns_lookup=no
		else
			printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" >&2
			error_message
		fi
	fi
}

function verify_pam_svc_list {
	svc_list=$1
	if echo $1 | egrep -s '^([^:,]+:)?(first|only|optional)(,([^:,]+:)?(first|only|optional))*$'; then
		:
	else
		printf "\n$(gettext "Invalid PAM service list '%s', exiting").\n" $svc_list >&2
		error_message
	fi
}

# Checks kdcs, returns a subset of resolvable kdcs
function verify_kdcs {
	typeset k_list="${1//,/ }"
	typeset no_ping_test=$2
	typeset -l kdc
	typeset kdc_up_list verified_kdc_list

	# If k_list is empty or only contains spaces error out
	if [[ -z $k_list || -z ${k_list// /} ]]; then
		printf "\n$(gettext "At least one %s should be listed").\n\n" "KDC" >&2
		usage
	fi

	for kdc in $k_list; do
		# Try doing a A(ddress) record lookup
		fkdc=$($KLOOKUP $kdc)
		# If that doesn't work try CNAME
		[[ -z $fkdc ]] && fkdc=$($KLOOKUP $kdc C)
		if [[ -z $fkdc ]]; then
			printf "\n$(gettext "%s %s full host name not found, no action performed").\n" "KDC" $kdc >&2
		else
			if [[ -z $no_ping_test ]]; then
				if ping $fkdc 2 > /dev/null; then
					kdc_up_list="$kdc_up_list $fkdc"
				else
					printf "\n$(gettext "Warning: %s %s is unreachable.").\n" "KDC" $fkdc >&2
				fi
			fi
			verified_kdc_list="$verified_kdc_list $kdc"
		fi
	done

	if [[ -z $verified_kdc_list ]]; then
		printf "\n$(gettext "Unable to resolve the full host name of any %s.").\n\n" "KDC" >&2
		[[ -z $no_ping_test ]] && usage
	fi

	[[ -z $no_ping_test ]] && print -n "$verified_kdc_list"
}

function parse_service {
	typeset service_list=$1

	service_list=${service_list//,/ }
	for service in $service_list; do
		svc=${service%:}
		auth_type=${service#:}
		[[ -z $svc || -z $auth_type ]] && return
		print -- $svc $auth_type
	done
}

function verify_fqdnlist {
	typeset list="$1"
	typeset -l hostname
	typeset -i count=1
	typeset eachfqdn tmpvar fullhost

	list=$(echo "$list" | tr -d " " | tr -d "\t")
	hostname=$(uname -n | cut -d"." -f1)
	fqdnlist=$client_machine

	eachfqdn=$(echo "$list" | cut -d"," -f$count)
	if [[ -z $eachfqdn ]]; then
		printf "\n$(gettext "If the -f option is used, at least one FQDN should be listed").\n\n" >&2
		usage
	else
		while [[ ! -z $eachfqdn ]]; do
			tmpvar=$(echo "$eachfqdn" | cut -d"." -f1)
			if [[ -z $tmpvar ]]; then
				fullhost="$hostname$eachfqdn"
			else
				fullhost="$hostname.$eachfqdn"
			fi

			ping_check $fullhost $(gettext "System")
			if [[ $fullhost == $client_machine ]]; then
				:
			else
				fqdnlist="$fqdnlist $fullhost"
			fi

			if [[ $list == *,* ]]; then
				((count = count + 1))
				eachfqdn=$(echo "$list" | cut -d"," -f$count)
			else
				break
			fi
		done
	fi
}

function setup_keytab {
	typeset cname

	#
	# 1. kinit with ADMIN_PRINC
	#

	if [[ -z $ADMIN_PRINC ]]; then
		printf "\n$(gettext "Enter the krb5 administrative principal to be used"): "
		read ADMIN_PRINC
		checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
	fi

	echo "$ADMIN_PRINC">$TMP_FILE

	[[ $msad == yes ]] && return
	if egrep -s '\/admin' $TMP_FILE; then
		# Already in "/admin" format, do nothing
		:
	else
		if egrep -s '\/' $TMP_FILE; then
			printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n" >&2
			error_message
		else
			ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin")
		fi
	fi

	printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC

	cname=$(canon_resolve $KDC)
	if [[ -n $cname ]]; then
		kinit -S kadmin/$cname $ADMIN_PRINC
	else
		kinit -S kadmin/$FKDC $ADMIN_PRINC
	fi
	klist 1>$TMP_FILE 2>&1
	if egrep -s "$(gettext "Valid starting")" $TMP_FILE && egrep -s "kadmin/$FKDC@$realm" $TMP_FILE; then
    		:
	else
		printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC >&2
		error_message
	fi

	#
	# 2. Do we want to create and/or add service principal(s) for fqdn's
	#    other than the one listed in resolv.conf(4) ?
	#
	if [[ -z $options ]]; then
		query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?"
		if [[ $answer == yes ]]; then
			printf "$(gettext "Enter a comma-separated list of DNS domain names"): "
			read fqdnlist
			verify_fqdnlist "$fqdnlist"
		else
			fqdnlist=$client_machine
		fi
	else
		if [[ -z $fqdnlist ]]; then
			fqdnlist=$client_machine
		fi
	fi

	if [[ $add_nfs == yes ]]; then
		echo; call_kadmin nfs
	fi

	# Add the host entry to the keytab
	echo; call_kadmin host

}

function setup_lhn {
	typeset -l lhn=$logical_hn

	echo "$lhn" > $TMP_FILE
	if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then
		# do nothing, lhn is in fqdn format
		:
	else
		if egrep -s '\.+' $TMP_FILE; then
			printf "\n$(gettext "Improper format of logical hostname, exiting").\n" >&2
			error_message
		else
			# Attach fqdn to logical_hn, to get the Fully Qualified
			# Host Name of the client requested
			lhn=$(echo "$lhn.$domain")
		fi
	fi

	client_machine=$lhn

	ping_check $client_machine $(gettext "System")
}

function usage {
	printf "\n$(gettext "Usage: kclient [ options ]")\n" >&2
	printf "\t$(gettext "where options are any of the following")\n\n" >&2
	printf "\t$(gettext "[ -D domain_list ]  configure a client that has multiple mappings of domain and/or hosts to the default realm")\n" >&2
	printf "\t$(gettext "[ -K ]  configure a client that does not have host/service keys")\n" >&2
	printf "\t$(gettext "[ -R realm ]  specifies the realm to use")\n" >&2
	printf "\t$(gettext "[ -T kdc_vendor ]  specifies which KDC vendor is the server")\n" >&2
	printf "\t$(gettext "[ -a adminuser ]  specifies the Kerberos administrator")\n" >&2
	printf "\t$(gettext "[ -c filepath ]  specifies the krb5.conf path used to configure this client")\n" >&2
	printf "\t$(gettext "[ -d dnsarg ]  specifies which information should be looked up in DNS (dns_lookup_kdc, dns_lookup_realm, and dns_fallback)")\n" >&2
	printf "\t$(gettext "[ -f fqdn_list ]  specifies which domains to configure host keys for this client")\n" >&2
	printf "\t$(gettext "[ -h logicalhostname ]  configure the logical host name for a client that is in a cluster")\n" >&2
	printf "\t$(gettext "[ -k kdc_list ]  specify multiple KDCs, if -m is not used the first KDC in the list is assumed to be the master.  KDC host names are used verbatim.")\n" >&2
	printf "\t$(gettext "[ -m master_kdc_list ]  list of master KDC server host names")\n" >&2
	printf "\t$(gettext "[ -n ]  configure client to be an NFS client")\n" >&2
	printf "\t$(gettext "[ -p profile ]  specifies which profile file to use to configure this client")\n" >&2
	printf "\t$(gettext "[ -s pam_list ]  update the service for Kerberos authentication")\n" >&2
	printf "\t$(gettext "[ -x output_profile ]  creates configuration profile for use with AI, doesn't configure current host ")\n" >&2
	error_message
}

function discover_domain {
	typeset dom DOMs

	if [[ -z $realm ]]; then
		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs V`
	else
		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs.$realm V`
	fi

	[[ -z ${DOMs[0]} ]] && return 1

	dom=${DOMs[0]}

	dom=${dom#_ldap._tcp.dc._msdcs.}

	print -- $dom
}

function check_nss_hosts_or_ipnodes_config {
	typeset backend

	for backend in $1
	do
		[[ $backend == dns ]] && return 0
	done
	return 1
}

function check_nss_conf {
	typeset i j hosts_config

	for i in hosts ipnodes
	do
		grep "^${i}:" /etc/nsswitch.conf|read j hosts_config
		check_nss_hosts_or_ipnodes_config "$hosts_config" || return 1
	done

	return 0
}

function rev_resolve {
	typeset name ip

	ip=$(print -n `$KLOOKUP $1 I`)
	[[ -z $ip ]] && return
	for i in $ip
	do
		if ping $i 2 > /dev/null 2>&1; then
			break
		else
			i=
		fi
	done

	[[ -z $i ]] && return
	name=`$KLOOKUP $i P`
	[[ -z $name ]] && return

	print -- "$name"
}

function canon_resolve {
	typeset name ip

	name=`$KLOOKUP $1 C`
	[[ -z $name ]] && name=`$KLOOKUP $1 A`
	[[ -z $name ]] && return

	cname=$(rev_resolve $name)
	[[ -z $cname ]] && return

	print -- "$cname"
}

# Convert an AD-style domain DN to a DNS domainname
function dn2dns {
	typeset OIFS dname dn comp components

	dn=$1
	dname=

	OIFS="$IFS"
	IFS=,
	set -A components -- $1
	IFS="$OIFS"

	for comp in "${components[@]}"
	do
		[[ "$comp" == [dD][cC]=* ]] || continue
		dname="$dname.${comp#??=}"
	done

	print ${dname#.}
}

# Form a base DN from a DNS domainname and container
function getBaseDN {
	if [[ -n "$2" ]]
	then
		baseDN="CN=$1,$(dns2dn $2)"
	else
		baseDN="$(dns2dn $2)"
	fi
}

# Convert a DNS domainname to an AD-style DN for that domain
function dns2dn {
	typeset OIFS dn labels

	OIFS="$IFS"
	IFS=.
	set -A labels -- $1
	IFS="$OIFS"

	dn=
	for label in "${labels[@]}"
	do
		dn="${dn},DC=$label"
	done

	print -- "${dn#,}"
}

function getSRVs {
	typeset srv port

	$KLOOKUP $1 S | while read srv port
	do
		if ping $srv 2 > /dev/null 2>&1; then
			print -- $srv $port
		fi
	done
}

function getKDC {
	typeset j dom=$1

	set -A KPWs -- $(getSRVs _kpasswd._tcp.$dom.)
	kpasswd=${KPWs[0]}

	if [[ -n $siteName ]]
	then
		set -A KDCs -- $(getSRVs _kerberos._tcp.$siteName._sites.$dom.)
		kdc=${KDCs[0]}
		[[ -n $kdc ]] && return
	fi

	# No site name
	set -A KDCs -- $(getSRVs _kerberos._tcp.$dom.)
	kdc=${KDCs[0]}
	[[ -n $kdc ]] && return

	# Default
	set -A KDCs -- $DomainDnsZones 88
	kdc=$ForestDnsZones
}

function getDC {
	typeset j dom=$1

	if [[ -n $siteName ]]
	then
		set -A DCs -- $(getSRVs _ldap._tcp.$siteName._sites.dc._msdcs.$dom.)
		dc=${DCs[0]}
		[[ -n $dc ]] && return
	fi

	# No site name
	set -A DCs -- $(getSRVs _ldap._tcp.dc._msdcs.$dom.)
	dc=${DCs[0]}
	[[ -n $dc ]] && return

	# Default
	set -A DCs -- $DomainDnsZones 389
	dc=$DomainDnsZones
}

function write_ads_krb5conf {
	typeset kdcs dom=$1

	printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE

	for i in ${KDCs[@]}
	do
		[[ $i == +([0-9]) ]] && continue
		if [[ -n $kdcs ]]
		then
			kdcs="$kdcs,$i"
		else
			kdcs=$i
		fi
	done

	$KCONF -f $KRB5_CONFIG -r $realm -k $kdcs -m $KDC -p SET_CHANGE -d .$dom

	if [[ $? -ne 0 ]]; then
		printf "\n$(gettext "Can not update %s, exiting").\n" $KRB5_CONFIG >&2
		error_message
	fi
}

function getForestName {
	ldapsearch -e -R -T -h $dc $ldap_args \
	    -b "" -s base "" schemaNamingContext| \
		grep ^schemaNamingContext|read j schemaNamingContext

	if [[ $? -ne 0 ]]; then
		printf "$(gettext "Can't find forest").\n" >&2
		error_message
	fi
	schemaNamingContext=${schemaNamingContext#CN=Schema,CN=Configuration,}

	[[ -z $schemaNamingContext ]] && return 1

	forest=
	while [[ -n $schemaNamingContext ]]
	do
		schemaNamingContext=${schemaNamingContext#DC=}
		forest=${forest}.${schemaNamingContext%%,*}
		[[ "$schemaNamingContext" = *,* ]] || break
		schemaNamingContext=${schemaNamingContext#*,}
	done
	forest=${forest#.}
}

function getGC {
	typeset j

	[[ -n $gc ]] && return 0

	if [[ -n $siteName ]]
	then
		set -A GCs -- $(getSRVs _ldap._tcp.$siteName._sites.gc._msdcs.$forest.)
		gc=${GCs[0]}
		[[ -n $gc ]] && return
	fi

	# No site name
	set -A GCs -- $(getSRVs _ldap._tcp.gc._msdcs.$forest.)
	gc=${GCs[0]}
	[[ -n $gc ]] && return

	# Default
	set -A GCs -- $ForestDnsZones 3268
	gc=$ForestDnsZones
}

#
# The local variables used to calculate the IP address are of type unsigned
# integer (-ui), as this is required to restrict the integer to 32b.
# Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
#
function ipAddr2num {
	typeset OIFS
	typeset -ui16 num

	if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]]
	then
		print 0
		return 0
	fi

	OIFS="$IFS"
	IFS=.
	set -- $1
	IFS="$OIFS"

	num=$((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4}))

	print -- $num
}

#
# The local variables used to calculate the IP address are of type unsigned
# integer (-ui), as this is required to restrict the integer to 32b.
# Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
#
function num2ipAddr {
	typeset -ui16 num
	typeset -ui10 a b c d

	num=$1
	a=$((num>>24        ))
	b=$((num>>16 & 16#ff))
	c=$((num>>8  & 16#ff))
	d=$((num     & 16#ff))
	print -- $a.$b.$c.$d
}

#
# The local variables used to calculate the IP address are of type unsigned
# integer (-ui), as this is required to restrict the integer to 32b.
# Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
#
function netmask2length {
	typeset -ui16 netmask
	typeset -i len

	netmask=$1
	len=32
	while [[ $((netmask % 2)) -eq 0 ]]
	do
		netmask=$((netmask>>1))
		len=$((len - 1))
	done
	print $len
}

#
# The local variables used to calculate the IP address are of type unsigned
# integer (-ui), as this is required to restrict the integer to 32b.
# Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
#
function getSubnets {
	typeset -ui16 addr netmask
	typeset -ui16 classa=16\#ff000000

	ifconfig -a|while read line
	do
		addr=0
		netmask=0
		set -- $line
		[[ $1 == inet ]] || continue
		while [[ $# -gt 0 ]]
		do
			case "$1" in
				inet) addr=$(ipAddr2num $2); shift;;
				netmask) eval netmask=16\#$2; shift;;
				*) :;
			esac
			shift
		done

		[[ $addr -eq 0 || $netmask -eq 0 ]] && continue
		[[ $((addr & classa)) -eq 16\#7f000000 ]] && continue

		print $(num2ipAddr $((addr & netmask)))/$(netmask2length $netmask)
	done
}

function getSite {
	typeset subnet siteDN j ldapsrv subnet_dom

	eval "[[ -n \"\$siteName\" ]]" && return
	for subnet in $(getSubnets)
	do
		ldapsearch -e -R -T -h $dc $ldap_args \
		    -p 3268 -b "" -s sub cn=$subnet dn |grep ^dn|read j subnetDN

		[[ -z $subnetDN ]] && continue
		subnet_dom=$(dn2dns $subnetDN)
		ldapsrv=$(canon_resolve DomainDnsZones.$subnet_dom)
		[[ -z $ldapsrv ]] && continue
		ldapsearch -e -R -T -h $ldapsrv $ldap_args \
		    -b "$subnetDN" -s base "" siteObject \
		    |grep ^siteObject|read j siteDN

		[[ -z $siteDN ]] && continue

		eval siteName=${siteDN%%,*}
		eval siteName=\${siteName#CN=}
		return
	done
}

function doKRB5config {
	[[ -f $KRB5_CONFIG_FILE ]] && \
		cp $KRB5_CONFIG_FILE ${KRB5_CONFIG_FILE}-pre-kclient

	[[ -f $KRB5_KEYTAB_FILE ]] && \
		cp $KRB5_KEYTAB_FILE ${KRB5_KEYTAB_FILE}-pre-kclient

	[[ -s $KRB5_CONFIG ]] && cp $KRB5_CONFIG $KRB5_CONFIG_FILE
	[[ -s $KRB5_CONFIG_FILE ]] && chmod 0644 $KRB5_CONFIG_FILE
	[[ -s $new_keytab ]] && cp $new_keytab $KRB5_KEYTAB_FILE
	[[ -s $KRB5_KEYTAB_FILE ]] && chmod 0600 $KRB5_KEYTAB_FILE
}

function addDNSRR {
	typeset dom=$1

	ddnsProp=ddns_enable

	ddns_enable=`sharectl get -p $ddnsProp smb`

	if [[ $ddns_enable != "$ddnsProp=true" ]]; then
		printf "$(gettext "Warning: won't create DNS records for client").\n"
		printf "$(gettext "%s property not set to 'true' through sharectl(1M)").\n" $ddnsProp
		return
	fi
	
	# Destroy any existing ccache as GSS_C_NO_CREDENTIAL will pick up any
	# residual default credential in the cache.
	kdestroy > /dev/null 2>&1

	$KDYNDNS -d $dom > /dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		#
		# Non-fatal, we should carry-on as clients may resolve to
		# different servers and the client could already exist there.
		#
		printf "$(gettext "Warning: unable to create DNS records for client").\n"
		printf "$(gettext "This could mean that '%s' is not included as a 'nameserver' in the /etc/resolv.conf file or some other type of error").\n" $dc
	fi
}

function setSMB {
	typeset domain=$1
	typeset server=$2

	smbSvrFMRI=svc:/network/smb/server
	smbFMRI=svc:/network/smb
	secProp=smb/security

	check_bin $KSMB
	check_bin $KDYNDNS
		
	svccfg -s $smbFMRI setprop $secProp=domain
	svcadm refresh $smbFMRI

	printf "%s" "$newpw" | $KSMB -d $domain -s $server
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "Warning: unable to set %s domain, server and password information").\n" smb
		return
	fi

	svcadm restart $smbSvrFMRI > /dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "Warning: unable to restart %s").\n" $smbSvrFMRI
	fi
}

function compareDomains {
	typeset oldDom hspn newDom=$1

	# If the client has been previously configured in a different
	# realm/domain then we need to prompt the user to see if they wish to
	# switch domains.
	klist -k 2>&1 | grep @ | read j hspn
	[[ -z $hspn ]] && return

	oldDom=${hspn#*@}
	if [[ $oldDom != $newDom ]]; then
		printf "$(gettext "The client is currently configured in a different domain").\n"
		printf "$(gettext "Currently in the '%s' domain, trying to join the '%s' domain").\n" $oldDom $newDom
		query "$(gettext "Do you want the client to join a new domain") ?"
		printf "\n"
		if [[ $answer != yes ]]; then
			printf "$(gettext "Client will not be joined to the new domain").\n" >&2
			error_message
		fi
	fi
}

function getKDCDC {
	typeset dom=$1

	getKDC $dom
	if [[ -n $kdc ]]; then
		KDC=$kdc
		dc=$kdc
	else
		getDC $dom
		if [[ -n $dc ]]; then
			KDC=$dc
		else
			printf "$(gettext "Could not find domain controller server for '%s'.  Exiting").\n" $realm >&2
			error_message
		fi
	fi
}

function gen_rand {
	typeset -u hex

	dd if=/dev/random bs=1 count=1 2>/dev/null | od -A n -tx1 | read hex

	printf %s $((16#$hex))
}

function enable_warnd_srv {
	#
	# Start ktkt_warn service if it is disabled
	#
	typeset ktkt_srv_state="$(svcs -H -o STATE svc:/network/security/ktkt_warn:default)"

	case "$ktkt_srv_state" in
		'online') ;; # nothing needs to be done

		'disabled')
			svcadm enable -s svc:/network/security/ktkt_warn:default
			[[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable ktkt_warn service").\n";;

		*) printf "$(gettext "Warning: the ktkt_warn service is in %s state but should be enabled.").\n $ktkt_srv_state";;
	esac
}

function join_domain {
	typeset -u upcase_nodename
	typeset -l locase_nodename domain
	typeset -L15 string15
	typeset netbios_nodename fqdn
	typeset ldap_error

	container=Computers
	ldap_args="-o authzid= -o mech=gssapi"
	userAccountControlBASE=4096

	if [[ -z $ADMIN_PRINC ]]; then
		cprinc=Administrator
	else
		cprinc=$ADMIN_PRINC
	fi

	domain=$(discover_domain)
	if [[ -z $domain ]]; then
		printf "$(gettext "Can not find realm") '%s'.\n" $realm >&2
		error_message
	fi

	realm=$domain

	if [[ ${#hostname} -gt 15 ]]; then
		string15=$hostname
		upcase_nodename=$string15
		locase_nodename=$string15
	else
		upcase_nodename=$hostname
		locase_nodename=$hostname
	fi

	netbios_nodename="${upcase_nodename}\$"
	fqdn=$hostname.$domain
	upn=host/${fqdn}@${realm}

	object=$(mktemp -q -t kclient-computer-object.XXXXXX)
	if [[ -z $object ]]; then
		printf "\n$(gettext "Can not create temporary file, exiting").\n
" >&2
		error_message
        fi

	modify_existing=false
	recreate=false

	DomainDnsZones=$(rev_resolve DomainDnsZones.$domain.)
	ForestDnsZones=$(rev_resolve ForestDnsZones.$domain.)

	getBaseDN "$container" "$domain"

	if [[ -n $KDC ]]; then
		dc=$KDC
	else
		getKDCDC $domain
	fi

	write_ads_krb5conf $domain

	printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm
	
	if [[ -z $key_b64 ]]; then
		kinit $cprinc@$realm
	else
		tmp_keytab=$(mktemp -q -t kclient-adminkeytab.XXXXXX) 
		$KT2PROF -d -p 'autoreg/key' -k $tmp_keytab
		kinit -kt $tmp_keytab $cprinc@$realm
	fi
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "Could not authenticate %s.  Exiting").\n" $cprinc@$realm >&2
		[[ -f $tmp_keytab ]] && rm -f $tmp_keytab
		error_message
	fi

	if getForestName
	then
		printf "\n$(gettext "Forest name found: %s")\n\n" $forest
	else
		printf "\n$(gettext "Forest name not found, assuming forest is the domain name").\n"
	fi

	getGC
	getSite

	if [[ -z $siteName ]]
	then
    		printf "$(gettext "Site name not found.  Local DCs/GCs will not be discovered").\n\n"
	else
    		printf "$(gettext "Looking for _local_ KDCs, DCs and global catalog servers (SRV RRs)").\n"
		getKDCDC $domain
		getGC

		write_ads_krb5conf $domain
	fi

	if [[ ${#GCs} -eq 0 ]]; then
		printf "$(gettext "Could not find global catalogs.  Exiting").\n" >&2
		error_message
	fi

	# Check to see if the client is transitioning between domains.
	compareDomains $realm

	# Here we check domainFunctionality to see which release:
	# 0, 1, 2: Windows 2000, 2003 Interim, 2003 respecitively
	# 3: Windows 2008
	level=0
	ldapsearch -e -R -T -h "$dc" $ldap_args -b "" -s base "" \
	 domainControllerFunctionality| grep ^domainControllerFunctionality| \
	 read j level
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "Search for domain functionality failed, exiting").\n" >&2
		error_message
	fi

	if ldapsearch -e -R -T -h "$dc" $ldap_args -b "$baseDN" \
	    -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1
	then
		:
	else
		printf "$(gettext "Search for node failed, exiting").\n" >&2
		error_message
	fi
	ldapsearch -e -R -T -h "$dc" $ldap_args -b "$baseDN" -s sub \
	    sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn

	if [[ -z $dn ]]; then
		: # modify_existing is already false, which is what we want.
	elif [[ -n $key_b64 ]]; then
		modify_existing=true
	else
		printf "$(gettext "Computer account '%s' already exists in the '%s' domain").\n" $upcase_nodename $realm
		query "$(gettext "Do you wish to recreate this computer account") ?"
		printf "\n"
		if [[ $answer == yes ]]; then
			recreate=true
		else
			modify_existing=true
		fi
	fi

	if [[ $modify_existing == false && -n $dn ]]; then
		query "$(gettext "Would you like to delete any sub-object found for this computer account") ?"
		if [[ $answer == yes ]]; then
			printf "$(gettext "Looking to see if the machine account contains other objects")...\n"
			ldapsearch -e -R -T -h "$dc" $ldap_args -b "$dn" -s sub "" dn | while read j sub_dn
			do
				[[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue
				if $recreate; then
					printf "$(gettext "Deleting the following object: %s")\n" ${sub_dn#$dn}
					ldapdelete -h "$dc" $ldap_args "$sub_dn" > /dev/null 2>&1
					if [[ $? -ne 0 ]]; then
						printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn}
					fi
				else
					printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn}
				fi
			done
		fi

		if $recreate; then
			ldapdelete -h "$dc" $ldap_args "$dn" > /dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} >&2
				error_message
			fi
		elif $modify_existing; then
			: # Nothing to delete
		else
			printf "$(gettext "A machine account already exists").\n" >&2
			error_message
		fi
	fi

	[[ -z $dn ]] && dn="CN=${upcase_nodename},${baseDN}"
	if $modify_existing; then
		cat > "$object" <<EOF
dn: $dn
changetype: modify
replace: userPrincipalName
userPrincipalName: $upn
-
replace: servicePrincipalName
servicePrincipalName: host/${fqdn}
-
replace: userAccountControl
userAccountControl: $((userAccountControlBASE + 32 + 2))
-
replace: dNSHostname
dNSHostname: ${fqdn}
EOF

		printf "$(gettext "A machine account already exists; updating it").\n"
		ldap_error=$(ldapadd -h "$dc" $ldap_args -f "$object" 2>&1)
		if [[ $? -ne 0 ]]; then
			printf "$(gettext "Failed to modify the AD object via LDAP: %s").\n" "$ldap_error" >&2
			error_message
		fi
	else
		dn="CN=${upcase_nodename},${baseDN}"
		cat > "$object" <<EOF
dn: $dn
objectClass: computer
cn: $upcase_nodename
sAMAccountName: ${netbios_nodename}
userPrincipalName: $upn
servicePrincipalName: host/${fqdn}
userAccountControl: $((userAccountControlBASE + 32 + 2))
dNSHostname: ${fqdn}
EOF

		printf "$(gettext "Creating the machine account in AD via LDAP").\n\n"

		ldap_error=$(ldapadd -h "$dc" $ldap_args -f "$object" 2>&1)
		if [[ $? -ne 0 ]]; then
			printf "$(gettext "Failed to create the AD object via LDAP: %s").\n" "$ldap_error" >&2
			error_message
		fi
	fi

	# Generate a new password for the new account
	MAX_PASS=120
        i=0

	# first check to see if /dev/random exists to generate a new password
	if [[ ! -r /dev/random ]]; then
		printf "$(gettext "/dev/random does not exist").\n" >&2
		error_message
	fi

	while ((MAX_PASS > i))
	do
		# [MS-DISO] A machine password is an ASCII string of randomly
		# chosen characters. Each character's ASCII code is between 32
		# and 122 inclusive.
		c=$(printf "\\$(printf %o $(($(gen_rand) % 91 + 32)))\n")
		p="$p$c"
		((i+=1))
	done

	newpw=$p
	if [[ ${#newpw} -ne MAX_PASS ]]; then
		printf "$(gettext "Password created was of incorrect length").\n" >&2
		error_message
	fi

	# Set the new password
	printf "%s" "$newpw" | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1
	if [[ $? -ne 0 ]]
	then
		printf "$(gettext "Failed to set account password").\n" >&2
		error_message
	fi

	# Lookup the new principal's kvno:
	ldapsearch -e -R -T -h "$dc" $ldap_args -b "$baseDN" \
		 -s sub cn=$upcase_nodename msDS-KeyVersionNumber| \
		grep "^msDS-KeyVersionNumber"|read j kvno
	[[ -z $kvno ]] && kvno=1

	# Set supported enctypes.  This only works for Longhorn/Vista, so we
	# ignore errors here.
	userAccountControl=$((userAccountControlBASE + 524288 + 65536))
	set -A enctypes --

	# Do we have local support for AES?
	encrypt -l|grep ^aes|read j minkeysize maxkeysize
	val=
	if [[ $maxkeysize -eq 256 ]]; then
		val=16
		enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96
	fi
	if [[ $minkeysize -eq 128 ]]; then
		((val=val+8))
		enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96
	fi

	# RC4 comes next (whether it's better than 1DES or not -- AD prefers it)
	if encrypt -l|grep -q ^arcfour
	then
		((val=val+4))
		enctypes[${#enctypes[@]}]=arcfour-hmac-md5
	else
		# Use 1DES ONLY if we don't have arcfour
		userAccountControl=$((userAccountControl + 2097152))
	fi
	if encrypt -l|grep -q ^des
	then
		((val=val+2))
		enctypes[${#enctypes[@]}]=des-cbc-md5
	fi

	if [[ ${#enctypes[@]} -eq 0 ]]
	then
		printf "$(gettext "No enctypes are supported").\n"
		printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(1M)").\n" >&2
		error_message
	fi

	# If domain crontroller is Longhorn or above then set new supported
	# encryption type attributes.
	if [[ $level -gt 2 ]]; then
		cat > "$object" <<EOF
dn: $dn
changetype: modify
replace: msDS-SupportedEncryptionTypes
msDS-SupportedEncryptionTypes: $val
EOF
		ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
		if [[ $? -ne 0 ]]; then
			printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n"
		fi
	fi

	# We should probably check whether arcfour is available, and if not,
	# then set the 1DES only flag, but whatever, it's not likely NOT to be
	# available on S10/Nevada!

	# Reset userAccountControl
	#
	#  NORMAL_ACCOUNT (512) | DONT_EXPIRE_PASSWORD (65536) |
	#  TRUSTED_FOR_DELEGATION (524288)
	#
	# and possibly UseDesOnly (2097152) (see above)
	#
	cat > "$object" <<EOF
dn: $dn
changetype: modify
replace: userAccountControl
userAccountControl: $userAccountControl
EOF
	ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
		error_message
	fi

	# Setup a keytab file
	set -A args --
	for enctype in "${enctypes[@]}"
	do
		args[${#args[@]}]=-e
		args[${#args[@]}]=$enctype
	done

	rm $new_keytab > /dev/null 2>&1

	cat > "$object" <<EOF
dn: $dn
changetype: modify
add: servicePrincipalName
servicePrincipalName: nfs/${fqdn}
servicePrincipalName: HTTP/${fqdn}
servicePrincipalName: root/${fqdn}
servicePrincipalName: cifs/${fqdn}
servicePrincipalName: host/${upcase_nodename}
EOF
	ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
		error_message
	fi

	#
	# In Windows, unlike MIT based implementations we salt the keys with
	# the UPN, which is based on the host/string15@realm elements, not
	# with the individual SPN strings.
	#
	salt=host/${locase_nodename}.${domain}@${realm}

	skeys=(host/${fqdn}@${realm} nfs/${fqdn}@${realm} HTTP/${fqdn}@${realm})
	skeys+=(root/${fqdn}@${realm} cifs/${fqdn}@${realm})
	skeys+=(${netbios_nodename}@${realm} host/${upcase_nodename}@${realm})
	skeys+=(cifs/${upcase_nodename}@${realm})

	ks_args="-n -s $salt -v $kvno -k $new_keytab ${args[@]}" 

	for skey in ${skeys[@]}
	do
		printf "%s" "$newpw" | $KSETPW $ks_args $skey > /dev/null 2>&1
		if [[ $? -ne 0 ]]
		then
			printf "$(gettext "Failed to set password").\n" >&2
			error_message
		fi
	done

	doKRB5config

	setSMB $domain $dc

	addDNSRR $domain

	enable_warnd_srv

	printf -- "---------------------------------------------------\n"
	printf "$(gettext "Setup COMPLETE").\n\n"

	kdestroy -q 1>$TMP_FILE 2>&1
	rm -f $TMP_FILE
	rm -rf $TMPDIR > /dev/null 2>&1

	exit 0
}

###########################
#	Main section	  #
###########################
#
# Set the Kerberos config file and some default strings/files
#

# ksh debuging
# trace all functions
#typeset -ft $(typeset -f)
# trace commands in main
#set -x

KRB5_CONFIG_FILE=/etc/krb5/krb5.conf
KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab
RESOLV_CONF_FILE=/etc/resolv.conf

KLOOKUP=/usr/lib/krb5/klookup;	check_bin $KLOOKUP
KSETPW=/usr/lib/krb5/ksetpw;	check_bin $KSETPW
KSMB=/usr/lib/krb5/ksmb;
KT2PROF=/usr/lib/krb5/kt2prof;	check_bin $KT2PROF
KDYNDNS=/usr/lib/krb5/kdyndns;
KCONF=/usr/lib/krb5/kconf;	check_bin $KCONF

unset MACHINE_THAT_GOES_PING

dns_lookup=no
adddns=no
no_keytab=no
checkval=""
profile=""
xml_profile=""
typeset -u realm
typeset -l hostname KDC

export TMPDIR="/system/volatile/kclient"
rm -rf $TMPDIR > /dev/null 2>&1

mkdir $TMPDIR > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
	printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2
	exit 1
fi

TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX)
export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX)
export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) 
new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) 
if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]]
then
	printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2
	exit 1
fi

#
# If we are interrupted, cleanup after ourselves
#
trap "exiting 1" HUP INT QUIT TERM

if [[ -d /usr/bin ]]; then
	if [[ -d /usr/sbin ]]; then
		PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
		export PATH
	else
		printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
		exit 1
	fi
else
	printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
	exit 1
fi

printf "\n$(gettext "Starting client setup")\n\n"
printf -- "---------------------------------------------------\n"

#
# Check for uid 0, disallow otherwise
#
uid=$(id -u)
if [[ $? -eq 0 ]]; then
	if [[ "$uid" != 0 ]]; then
		printf "\n$(gettext "Administrative privileges are required to run this script, exiting").\n" >&2
		error_message
	fi
else
	printf "\n$(gettext "uid check failed, exiting").\n" >&2
	error_message
fi

uname=$(uname -n)
hostname=${uname%%.*}

#
# Process the command-line arguments (if any)
#
OPTIND=1
while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T:x:S OPTIONS
do
	case $OPTIONS in
	    D) options="$options -D"
	       domain_list="$OPTARG"
	       ;;
	    K) options="$options -K"
	       no_keytab=yes
	       ;;
	    R) options="$options -R"
	       realm="$OPTARG"
	       checkval="REALM"; check_value $realm
	       ;;
	    T) options="$options -T"
	       kdctype="$OPTARG"
	       if [[ $kdctype == ms_ad ]]; then
		msad=yes
		adddns=yes
	       else
		non_solaris=yes
		no_keytab=yes
	       fi
	       ;;
            S) FMRI='system/kerberos/install:default'
	       key_b64=$(svcprop -p autoreg/key $FMRI 2>/dev/null)
	       if [[ $? -ne 0 || -z $key_b64 ]]; then
		       usage;
	       fi
	       ;;
	    a) options="$options -a"
	       ADMIN_PRINC="$OPTARG"
	       checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
	       ;;
	    c) options="$options -c"
	       filepath="$OPTARG"
	       ;;
	    d) options="$options -d"
	       dnsarg="$OPTARG"
	       checkval="DNS_OPTIONS"; check_value $dnsarg
	       ;;
	    f) options="$options -f"
	       fqdnlist="$OPTARG"
 	       ;;
	    h) options="$options -h"
	       logical_hn="$OPTARG"
	       checkval="LOGICAL_HOSTNAME"; check_value $logical_hn
	       ;;
	    k) options="$options -k"
	       kdc_list="${OPTARG//,/ }"
		# If kdc_list is empty or only contains spaces error out
		if [[ -z $kdc_list || -z ${kdc_list// /} ]]; then
			printf "\n$(gettext "At least one %s should be listed with -k").\n\n" "KDC" >&2
			usage
		fi
	       ;;
	    m) options="$options -m"
	       #KDC="$OPTARG"
	       master_kdc_list="${OPTARG//,/ }"
		# If master_kdc_list is empty or only contains spaces error out
		if [[ -z $master_kdc_list || -z ${master_kdc_list// /} ]]; then
			printf "\n$(gettext "At least one %s should be listed with -k").\n\n" "KDC" >&2
			usage
		fi
	       ;;
	    n) options="$options -n"
	       add_nfs=yes
	       ;;
	    p) options="$options -p"
	       profile="$OPTARG"
	       read_profile $profile
	       ;;
	    s) options="$options -s"
	       svc_list="$OPTARG"
	       verify_pam_svc_list $svc_list
	       SVCs=${svc_list//,/ }
 	       ;;
	    x) xml_profile="$OPTARG"
	       ;;
	    \?) usage
	       ;;
	    *) usage
	       ;;
	esac
done

#correct argument count after options
shift `expr $OPTIND - 1`

if [[ -z $options ]]; then
	:
else
	if [[ $# -ne 0 ]]; then
		usage
	fi
fi

if [[ -n $xml_profile ]]; then
	xml_profile_prompts
	write_xml_profile $xml_profile	# runs exec, never returns
	# unreachable
fi

#
# Check to see if we will be a client of a MIT, Heimdal, Shishi, etc.
#
if [[ -z $options ]]; then
	query "$(gettext "Is this a client of a non-Solaris KDC") ?"
	non_solaris=$answer
	if [[ $non_solaris == yes ]]; then
		printf "$(gettext "Which type of KDC is the server"):\n"
		printf "\t$(gettext "ms_ad: Microsoft Active Directory")\n"
		printf "\t$(gettext "mit: MIT KDC server")\n"
		printf "\t$(gettext "heimdal: Heimdal KDC server")\n"
		printf "\t$(gettext "shishi: Shishi KDC server")\n"
		printf "$(gettext "Enter required KDC type"): "
		read kdctype
		if [[ $kdctype == ms_ad ]]; then
			msad=yes
		elif [[ $kdctype == mit || $kdctype == heimdal || \
		    $kdctype == shishi ]]; then
			no_keytab=yes
		else
			printf "\n$(gettext "Invalid KDC type option, valid types are ms_ad, mit, heimdal, or shishi, exiting").\n" >&2
			error_message
		fi
	fi
fi

[[ $msad == yes ]] && join_domain

#
# Check for /etc/resolv.conf
#
if [[ -r $RESOLV_CONF_FILE ]]; then
	client_machine=`$KLOOKUP`

	if [[ $? -ne 0 ]]; then
		if [[ $adddns == no ]]; then
			printf "\n$(gettext "%s does not have a DNS record and is required for Kerberos setup")\n" $hostname >&2
			error_message
		fi

	else
		#
		# If client entry already exists then do not recreate it
		#
		adddns=no

		hostname=${client_machine%%.*}
		domain=${client_machine#*.}
	fi

	short_fqdn=${domain#*.*}
	short_fqdn=$(echo $short_fqdn | grep "\.")
else
	#
	# /etc/resolv.conf not present, exit ...
	#
	printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE >&2
	printf "$(gettext "Refer to resolv.conf(4), exiting").\n" >&2
	error_message
fi

check_nss_conf || printf "$(gettext "/etc/nsswitch.conf does not make use of DNS for hosts and/or ipnodes").\n"

[[ -n $fqdnlist ]] && verify_fqdnlist "$fqdnlist"

if [[ -z $dnsarg && (-z $options || -z $filepath) ]]; then
	query "$(gettext "Do you want to use DNS for kerberos lookups") ?"
	if [[ $answer == yes ]]; then
		printf "\n$(gettext "Valid DNS lookup options are dns_lookup_kdc, dns_lookup_realm,\nand dns_fallback. Refer krb5.conf(4) for further details").\n"
		printf "\n$(gettext "Enter required DNS option"): "
		read dnsarg
		checkval="DNS_OPTIONS"; check_value $dnsarg
		set_dns_value $dnsarg
	fi
else
	[[ -z $dnsarg ]] && dnsarg=none
	set_dns_value $dnsarg
fi

if [[ -n $kdc_list ]]; then
	if [[ -z $master_kdc_list ]]; then
		# assign first kdc to master kdc list, rest to slave list
		print $kdc_list|read master_kdc_list slave_kdc_list
	else
		# masters were already specified so this is just slaves
		slave_kdc_list="$kdc_list"
	fi
fi

if [[ -z $realm ]]; then
	printf "$(gettext "Enter the Kerberos realm"): "
	read realm
	checkval="REALM"; check_value $realm
fi

if [[ -z $master_kdc_list ]]; then
	printf "$(gettext "Specify the master KDCs for the above realm using a comma-separated list"): "
	read master_kdc_list
fi

if [[ -n $master_kdc_list ]]; then
	master_kdc_list=$(verify_kdcs "$master_kdc_list")
	# Much of this script wants to access one master kdc in the KDC variable
	# so KDC is the first in the list.
	print $master_kdc_list|read KDC junk
else
	printf "\n$(gettext "At least one %s should be entered").\n\n" "KDC" >&2
	error_message
fi

FKDC=$($KLOOKUP $KDC)

if [[ -z $slave_kdc_list && (-z $options || -z $filepath) ]]; then
	query "$(gettext "Do you have any slave KDC(s)") ?"
	if [[ $answer == yes ]]; then
		printf "$(gettext "Enter a comma-separated list of slave KDC host names"): "
		read slave_kdc_list
	fi
fi

[[ -n $slave_kdc_list ]] && slave_kdc_list=$(verify_kdcs "$slave_kdc_list")

found=false
for kdc in $slave_kdc_list $master_kdc_list; do
	if ping $kdc 2 > /dev/null; then
		found=true
		break
	fi
done
! $found && printf "\n$(gettext "Warning, no %s is reachable.").\n\n" "KDC" >&2

#
# Check to see if we will have a dynamic presence in the realm
#
if [[ -z $options ]]; then
	query "$(gettext "Will this client need service keys") ?"
	if [[ $answer == no ]]; then
		no_keytab=yes
	fi
fi

#
# Check to see if we are configuring the client to use a logical host name
# of a cluster environment
#
if [[ -z $options ]]; then
	query "$(gettext "Is this client a member of a cluster that uses a logical host name") ?"
	if [[ $answer == yes ]]; then
		printf "$(gettext "Specify the logical hostname of the cluster"): "
		read logical_hn
		checkval="LOGICAL_HOSTNAME"; check_value $logical_hn
	fi
fi

if [[ -n $logical_hn ]]; then
	setup_lhn
fi

if [[ -z $domain_list && (-z $options || -z $filepath) ]]; then
	query "$(gettext "Do you have multiple domains/hosts to map to realm %s"
) ?" $realm
	if [[ $answer == yes ]]; then
		printf "$(gettext "Enter a comma-separated list of domain/hosts
to map to the default realm"): "
		read domain_list
	fi
fi
[[ -n $domain_list ]] && domain_list=${domain_list//,/ }

#
# Start writing up the krb5.conf file, save the existing one
# if already present
#
writeup_krb5_conf

#
# Is this client going to use krb-nfs?  If so then we need to at least
# uncomment the krb5* sec flavors in nfssec.conf.
#
if [[ -z $options ]]; then
	query "$(gettext "Do you plan on doing Kerberized nfs") ?"
	add_nfs=$answer
fi

if [[ $add_nfs == yes ]]; then
	modify_nfssec_conf

	#	
	# We also want to enable gss as we now live in a SBD world
	#
	svcadm enable svc:/network/rpc/gss:default
	[[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable gss service").\n"
fi

if [[ -z $options ]]; then
	query "$(gettext "Do you want to update/add PAM per-service policy file(s)") ?"
	if [[ $answer == yes ]]; then
		printf "$(gettext "Enter a list of PAM service names in the following format: service:{first|only|optional}[,..]"): "
		read svc_list
		verify_pam_svc_list $svc_list
		SVCs=${svc_list//,/ }
	fi
fi
[[ -n $svc_list ]] && update_pam_conf

#
# Copy over krb5.conf master copy from filepath
#
if [[ -z $options || -z $filepath ]]; then
	query "$(gettext "Do you want to copy over the master krb5.conf file") ?"
	if [[ $answer == yes ]]; then
		printf "$(gettext "Enter the pathname of the file to be copied"): "
		read filepath
	fi
fi

if [[ -n $filepath && -r $filepath ]]; then
	cp $filepath $KRB5_CONFIG
	if [[ $? -eq 0 ]]; then
		printf "$(gettext "Copied %s to %s").\n" $filepath $KRB5_CONFIG
	else
		printf "$(gettext "Copy of %s failed, exiting").\n" $filepath >&2
		error_message
	fi
elif [[ -n $filepath ]]; then
	printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2
	error_message
fi

doKRB5config

#
# Populate any service keys needed for the client in the keytab file
#
if [[ $no_keytab != yes ]]; then
	setup_keytab
else
	printf "\n$(gettext "Note: %s file not created, please refer to verify_ap_req_nofail in krb5.conf(4) for the implications").\n" $KRB5_KEYTAB_FILE
	printf "$(gettext "Client will also not be able to host services that use Kerberos").\n"
fi

#
# Start ktkt_warn service if it is disabled
#
enable_warnd_srv

printf -- "\n---------------------------------------------------\n"
printf "$(gettext "Setup COMPLETE").\n\n"

#
# If we have configured the client in a cluster we need to remind the user
# to propagate the keytab and configuration files to the other members.
#
if [[ -n $logical_hn ]]; then
	printf "\n$(gettext "Note, you will need to securely transfer the /etc/krb5/krb5.keytab and /etc/krb5/krb5.conf files to all the other members of your cluster").\n"
fi

#
# Cleanup.
#
kdestroy -q 1>$TMP_FILE 2>&1
rm -f $TMP_FILE
rm -rf $TMPDIR > /dev/null 2>&1
exit 0