Home | History | Annotate | Download | only in kclient
      1 #!/bin/ksh93 -p
      2 #
      3 # CDDL HEADER START
      4 #
      5 # The contents of this file are subject to the terms of the
      6 # Common Development and Distribution License (the "License").
      7 # You may not use this file except in compliance with the License.
      8 #
      9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10 # or http://www.opensolaris.org/os/licensing.
     11 # See the License for the specific language governing permissions
     12 # and limitations under the License.
     13 #
     14 # When distributing Covered Code, include this CDDL HEADER in each
     15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16 # If applicable, add the following below this CDDL HEADER, with the
     17 # fields enclosed by brackets "[]" replaced with your own identifying
     18 # information: Portions Copyright [yyyy] [name of copyright owner]
     19 #
     20 # CDDL HEADER END
     21 #
     22 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23 # Use is subject to license terms.
     24 #
     25 # This script is used to setup the Kerberos client by
     26 # supplying information about the Kerberos realm and kdc.
     27 #
     28 # The kerberos configuration file (/etc/krb5/krb5.conf) would
     29 # be generated and local host's keytab file setup. The script
     30 # can also optionally setup the system to do kerberized nfs and
     31 # bringover a master krb5.conf copy from a specified location.
     32 #
     33 
     34 function cleanup {
     35 
     36 	kdestroy -q > $TMP_FILE 2>&1
     37 	rm -r $TMPDIR > /dev/null 2>&1
     38 
     39 	exit $1
     40 }
     41 function exiting {
     42 	
     43         printf "\n$(gettext "Exiting setup, nothing changed").\n\n"
     44 
     45 	cleanup $1
     46 }
     47 
     48 function error_message {
     49 
     50         printf -- "---------------------------------------------------\n" >&2
     51         printf "$(gettext "Setup FAILED").\n\n" >&2
     52 
     53         cleanup 1
     54 }
     55 
     56 function check_bin {
     57 
     58 	typeset bin=$1
     59 
     60 	if [[ ! -x $bin ]]; then
     61 		printf "$(gettext "Could not access/execute %s").\n" $bin >&2
     62 		error_message
     63 	fi
     64 }
     65 
     66 function cannot_create {
     67 	typeset filename="$1"
     68 	typeset stat="$2"
     69 
     70 	if [[ $stat -ne 0 ]]; then
     71 		printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2
     72 		error_message
     73 	fi
     74 }
     75 
     76 function update_pam_conf {
     77 	typeset PAM TPAM service
     78 
     79 	PAM=/etc/pam.conf
     80 
     81 	TPAM=$(mktemp -q -t kclient-pamconf.XXXXXX)
     82 	if [[ -z $TPAM ]]; then
     83 		printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2
     84 		error_message
     85 	fi
     86 
     87 	cp $PAM $TPAM >/dev/null 2>&1
     88 
     89 	printf "$(gettext "Configuring %s").\n\n" $PAM
     90 
     91 	for service in $SVCs; do
     92 		svc=${service%:*}
     93 		auth_type=${service#*:}
     94 		if egrep -s "^$svc[ 	][ 	]*auth.*pam_krb5*" $TPAM; then
     95 			printf "$(gettext "The %s service is already configured for pam_krb5, please merge this service in %s").\n\n" $svc $PAM >&2
     96 			continue
     97 		else
     98 			exec 3>>$TPAM
     99 			printf "\n$svc\tauth include\t\tpam_krb5_$auth_type\n" 1>&3
    100 		fi
    101 	done
    102 
    103 	cp $TPAM $PAM > /dev/null 2>&1
    104 
    105 	rm $TPAM > /dev/null 2>&1
    106 }
    107 
    108 function modify_nfssec_conf {
    109 	typeset NFSSEC_FILE="/etc/nfssec.conf"
    110 
    111 	if [[ -r $NFSSEC_FILE ]]; then
    112 		cat $NFSSEC_FILE > $NFSSEC_FILE.sav
    113 		cannot_create $NFSSEC_FILE.sav $?
    114 	fi
    115 
    116 	cat $NFSSEC_FILE > $TMP_FILE
    117 	cannot_create $TMP_FILE $?
    118 
    119 	if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1; then
    120 		sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE
    121 		cannot_create $NFSSEC_FILE $?
    122 	fi
    123 }
    124 
    125 function call_kadmin {
    126 	typeset svc="$1"
    127 	typeset bool1 bool2 bool3 bool4
    128 	typeset service_princ getprincsubcommand anksubcommand ktaddsubcommand
    129 	typeset ktremsubcommand
    130 
    131 	for listentry in $fqdnlist; do
    132 
    133 	# Reset conditional vars to 1
    134 	bool1=1; bool2=1; bool3=1; bool4=1
    135 
    136 	service_princ=$(echo "${svc}/${listentry}")
    137 	getprincsubcommand="getprinc $service_princ"
    138 	anksubcommand="addprinc -randkey $service_princ"
    139 	ktaddsubcommand="ktadd $service_princ"
    140 	ktremsubcommand="ktrem $service_princ all"
    141 
    142 	kadmin -c $KRB5CCNAME -q "$getprincsubcommand" 1>$TMP_FILE 2>&1
    143 
    144 	egrep -s "$(gettext "get_principal: Principal does not exist")" $TMP_FILE
    145 	bool1=$?
    146 	egrep -s "$(gettext "get_principal: Operation requires ``get")" $TMP_FILE
    147 	bool2=$?
    148 
    149 	if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then
    150 		kadmin -c $KRB5CCNAME -q "$anksubcommand" 1>$TMP_FILE 2>&1
    151 
    152 		egrep -s "$(gettext "add_principal: Principal or policy already exists while creating \"$service_princ@$realm\".")" $TMP_FILE
    153 		bool3=$?
    154 
    155 		egrep -s "$(gettext "Principal \"$service_princ@$realm\" created.")" $TMP_FILE
    156 		bool4=$?
    157 
    158 		if [[ $bool3 -eq 0 || $bool4 -eq 0 ]]; then
    159 			printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ
    160 		else
    161 			cat $TMP_FILE;
    162 			printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ >&2
    163 			error_message
    164 		fi
    165 	else
    166 		printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ >&2
    167 	fi
    168 
    169 	klist -k 1>$TMP_FILE 2>&1
    170 	egrep -s "$service_princ@$realm" $TMP_FILE
    171 	if [[ $? -eq 0 ]]; then
    172 		printf "$(gettext "%s entry already present in keytab").\n" $service_princ >&2
    173 		# Don't care is this succeeds or not, just need to replace old
    174 		# entries as it is assummed that the client is reinitialized
    175 		kadmin -c $KRB5CCNAME -q "$ktremsubcommand" 1>$TMP_FILE 2>&1
    176 	fi
    177 
    178 	kadmin -c $KRB5CCNAME -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1
    179 	egrep -s "$(gettext "added to keytab WRFILE:$KRB5_KEYTAB_FILE.")" $TMP_FILE
    180 	if [[ $? -ne 0 ]]; then
    181 		cat $TMP_FILE;
    182 		printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ >&2
    183 		error_message
    184 	else
    185 		printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ
    186 	fi
    187 
    188 	done
    189 }
    190 
    191 function writeup_krb5_conf {
    192 	typeset dh
    193 
    194 	printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE
    195 
    196 	exec 3>$KRB5_CONFIG
    197 	if [[ $? -ne 0 ]]; then
    198 		printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2
    199 		error_message
    200 	fi
    201 
    202 	printf "[libdefaults]\n" 1>&3
    203 	if [[ $no_keytab == yes ]]; then
    204 		printf "\tverify_ap_req_nofail = false\n" 1>&3
    205 	fi
    206 	if [[ $dns_lookup == yes ]]; then
    207 	    printf "\t$dnsarg = on\n" 1>&3
    208 	    if [[ $dnsarg == dns_lookup_kdc ]]; then
    209 		printf "\tdefault_realm = $realm\n" 1>&3
    210 		printf "\n[domain_realm]\n" 1>&3
    211 		if [[ -n $fkdc_list ]]; then
    212 			for kdc in $fkdc_list; do
    213 				printf "\t$kdc = $realm\n" 1>&3
    214 			done
    215 		fi
    216 		printf "\t$FKDC = $realm\n" 1>&3
    217 		printf "\t$client_machine = $realm\n" 1>&3
    218 		if [[ -z $short_fqdn ]]; then
    219 			printf "\t.$domain = $realm\n\n" 1>&3
    220 		else
    221 			printf "\t.$short_fqdn = $realm\n\n" 1>&3
    222 		fi
    223 		if [[ -n $domain_list ]]; then
    224 			for dh in $domain_list; do
    225 				printf "\t$dh = $realm\n" 1>&3
    226 			done
    227 		fi
    228 	    else
    229 		if [[ $dnsarg = dns_lookup_realm ]]; then
    230 		    printf "\tdefault_realm = $realm\n" 1>&3
    231 		    printf "\n[realms]\n" 1>&3
    232 		    printf "\t$realm = {\n" 1>&3
    233 		    if [[ -n $kdc_list ]]; then
    234 			for kdc in $kdc_list; do
    235 				printf "\t\tkdc = $kdc\n" 1>&3
    236 			done
    237 		    else
    238 		    	printf "\t\tkdc = $KDC\n" 1>&3
    239 		    fi
    240 		    printf "\t\tadmin_server = $KDC\n" 1>&3
    241 		    if [[ $non_solaris == yes ]]; then
    242 			printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3
    243 		    fi
    244 		    printf "\t}\n\n" 1>&3
    245 		else
    246 		    printf "\tdefault_realm = $realm\n\n" 1>&3
    247 		fi
    248 	    fi
    249 	else
    250 	    printf "\tdefault_realm = $realm\n\n" 1>&3
    251 
    252 	    printf "[realms]\n" 1>&3
    253 	    printf "\t$realm = {\n" 1>&3
    254 	    if [[ -n $kdc_list ]]; then
    255 		for kdc in $kdc_list; do
    256 			printf "\t\tkdc = $kdc\n" 1>&3
    257 		done
    258 	    else
    259 	    	printf "\t\tkdc = $KDC\n" 1>&3
    260 	    fi
    261 	    printf "\t\tadmin_server = $KDC\n" 1>&3
    262 	    if [[ $non_solaris == yes ]]; then
    263 	    	printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3
    264 	    fi
    265 	    printf "\t}\n\n" 1>&3
    266 
    267 	    printf "[domain_realm]\n" 1>&3
    268 	    if [[ -n $fkdc_list ]]; then
    269 		for kdc in $fkdc_list; do
    270 			printf "\t$kdc = $realm\n" 1>&3
    271 		done
    272 	    fi
    273 	    printf "\t$FKDC = $realm\n" 1>&3
    274 	    printf "\t$client_machine = $realm\n" 1>&3
    275 	    if [[ -z $short_fqdn ]]; then
    276 		printf "\t.$domain = $realm\n\n" 1>&3
    277 	    else
    278 		printf "\t.$short_fqdn = $realm\n\n" 1>&3
    279 	    fi
    280 	    if [[ -n $domain_list ]]; then
    281 		for dh in $domain_list; do
    282 			printf "\t$dh = $realm\n" 1>&3
    283 		done
    284 	    fi
    285 	fi
    286 
    287 	printf "[logging]\n" 1>&3
    288 	printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3
    289 	printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3
    290 	printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3
    291 
    292 	printf "[appdefaults]\n" 1>&3
    293 	printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n" 1>&3
    294 	if [[ $no_keytab == yes ]]; then
    295 		printf "\t\tno_addresses = true\n" 1>&3
    296 	fi
    297 	printf "\t}\n" 1>&3
    298 }
    299 
    300 function ask {
    301 	typeset question=$1
    302 	typeset default_answer=$2
    303 
    304 	if [[ -z $default_answer ]]; then
    305 		printf "$question :"
    306 	else
    307 		printf "$question [$default_answer]: "
    308 	fi
    309 	read answer
    310 	test -z "$answer" && answer="$default_answer"
    311 }
    312 
    313 function yesno {
    314 	typeset question="$1"
    315 
    316 	answer=
    317 	yn=`printf "$(gettext "y/n")"`
    318 	y=`printf "$(gettext "y")"`
    319 	n=`printf "$(gettext "n")"`
    320 	yes=`printf "$(gettext "yes")"`
    321 	no=`printf "$(gettext "no")"`
    322 
    323 	while [[ -z $answer ]]; do
    324 		ask "$question" $yn
    325 		case $answer in
    326 			$y|$yes)	answer=yes;;
    327 			$n|$no)		answer=no;;
    328 			*)		answer=;;
    329 		esac
    330 	done
    331 }
    332 
    333 function query {
    334 	yesno "$*"
    335 
    336 	if [[ $answer == no ]]; then
    337 		printf "\t$(gettext "No action performed").\n"
    338 	fi
    339 }
    340 
    341 
    342 function read_profile {
    343 	typeset param value
    344 	typeset file="$1"
    345 
    346 	if [[ ! -d $file && -r $file ]]; then
    347 		while read param value
    348 		do
    349 			case $param in
    350 			REALM)  if [[ -z $realm ]]; then
    351 					realm="$value"
    352 					checkval="REALM"; check_value $realm
    353 				fi
    354 				;;
    355 			KDC)    if [[ -z $KDC ]]; then
    356 					KDC="$value"
    357 					checkval="KDC"; check_value $KDC
    358 				fi
    359 				;;
    360 			ADMIN)  if [[ -z $ADMIN_PRINC ]]; then
    361 					ADMIN_PRINC="$value"
    362 					checkval="ADMIN_PRINC"
    363     					check_value $ADMIN_PRINC
    364 				fi
    365 				;;
    366 			FILEPATH)  if [[ -z $filepath ]]; then
    367 					filepath="$value"
    368 				   fi
    369 				   ;;
    370 			NFS)    if [[ -z $add_nfs ]]; then
    371 				    if [[ $value == 1 ]]; then
    372 					    add_nfs=yes
    373 				    else
    374 					    add_nfs=no
    375 				    fi
    376 				fi
    377 				;;
    378 			NOKEY)    if [[ -z $no_keytab ]]; then
    379 				    if [[ $value == 1 ]]; then
    380 					    no_keytab=yes
    381 				    else
    382 					    no_keytab=no
    383 				    fi
    384 				fi
    385 				;;
    386 			NOSOL)  if [[ -z $non_solaris ]]; then
    387 				    if [[ $value == 1 ]]; then
    388 					    non_solaris=yes
    389 					    no_keytab=yes
    390 				    else
    391 					    non_solaris=no
    392 				    fi
    393 				fi
    394 				;;
    395 			LHN)    if [[ -z $logical_hn ]]; then
    396 					logical_hn="$value"
    397 					checkval="LOGICAL_HOSTNAME"
    398     					check_value $logical_hn
    399 				fi
    400 				;;
    401 			DNSLOOKUP) if [[ -z $dnsarg ]]; then
    402 					dnsarg="$value"
    403 					checkval="DNS_OPTIONS"
    404 					check_value $dnsarg
    405 				   fi
    406 				   ;;
    407 			FQDN) if [[ -z $fqdnlist ]]; then
    408 					fqdnlist="$value"
    409 					checkval="FQDN"
    410 					check_value $fqdnlist
    411 					verify_fqdnlist "$fqdnlist"
    412 			      fi
    413 			      ;;
    414 			MSAD) if [[ -z $msad ]]; then
    415 				if [[ $value == 1 ]]; then
    416 					msad=yes
    417 					non_solaris=yes
    418 				else
    419 					msad=no
    420 				fi
    421 			      fi
    422 			      ;;
    423 			esac
    424 		done <$file
    425 	else
    426 		printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file >&2
    427 		error_message
    428 	fi
    429 }
    430 
    431 function ping_check {
    432 	typeset machine="$1"
    433 	typeset string="$2"
    434 
    435 	if ping $machine 2 > /dev/null 2>&1; then
    436 		:
    437 	else
    438 		printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine >&2
    439 		error_message
    440 	fi
    441 
    442 	# Output timesync warning if not using a profile, i.e. in
    443 	# interactive mode.
    444 	if [[ -z $profile && $string == KDC ]]; then
    445 		# It's difficult to sync up time with KDC esp. if in a
    446 		# zone so just print a warning about KDC time sync.
    447 		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
    448 		printf "$(gettext "Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n" >&2
    449 break
    450 	fi
    451 }
    452 
    453 function check_value {
    454 	typeset arg="$1"
    455 
    456 	if [[ -z $arg ]]; then
    457 		printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval >&2
    458 		error_message
    459 	else
    460 		echo "$arg" > $TMP_FILE
    461 		if egrep -s '[*$^#!]+' $TMP_FILE; then
    462 			printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval >&2
    463 			error_message
    464 		fi
    465 	fi
    466 }
    467 
    468 function set_dns_value {
    469 	typeset -l arg="$1"
    470 
    471 	if [[ $arg == dns_lookup_kdc  ||  $arg == dns_lookup_realm  || $arg == dns_fallback ]]; then
    472 		dns_lookup=yes
    473 	else
    474 		if [[ $arg == none ]]; then
    475 			dns_lookup=no
    476 		else
    477 			printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" >&2
    478 			error_message
    479 		fi
    480 	fi
    481 }
    482 
    483 function verify_kdcs {
    484 	typeset k_list="$1"
    485 	typeset -l kdc
    486 	typeset list fqhn f_list
    487 
    488 	kdc_list=$(echo "$k_list" | sed 's/,/ /g')
    489 
    490 	if [[ -z $k_list ]]; then
    491 		printf "\n$(gettext "At least one KDC should be listed").\n\n" >&2
    492 		usage
    493 	fi
    494 
    495 	for kdc in $k_list; do
    496 		if [[ $kdc != $KDC ]]; then
    497 			list="$list $kdc"
    498 			fkdc=`$KLOOKUP $kdc`
    499 			if ping $fkdc 2 > /dev/null; then
    500 				:
    501 			else
    502 				printf "\n$(gettext "%s %s is unreachable, no action performed").\n" "KDC" $fkdc >&2
    503 			fi
    504 			f_list="$f_list $fkdc"
    505 		fi
    506 	done
    507 
    508 	fkdc_list="$f_list"
    509 	kdc_list="$list"
    510 }
    511 
    512 function parse_service {
    513 	typeset service_list=$1
    514 
    515 	service_list=${service_list//,/ }
    516 	for service in $service_list; do
    517 		svc=${service%:}
    518 		auth_type=${service#:}
    519 		[[ -z $svc || -z $auth_type ]] && return
    520 		print -- $svc $auth_type
    521 	done
    522 }
    523 
    524 function verify_fqdnlist {
    525 	typeset list="$1"
    526 	typeset -l hostname
    527 	typeset -i count=1
    528 	typeset fqdnlist eachfqdn tmpvar fullhost
    529 
    530 	list=$(echo "$list" | tr -d " " | tr -d "\t")
    531 	hostname=$(uname -n | cut -d"." -f1)
    532 	fqdnlist=$client_machine
    533 
    534 	eachfqdn=$(echo "$list" | cut -d"," -f$count)
    535 	if [[ -z $eachfqdn ]]; then
    536 		printf "\n$(gettext "If the -f option is used, at least one FQDN should be listed").\n\n" >&2
    537 		usage
    538 	else
    539 		while [[ ! -z $eachfqdn ]]; do
    540 			tmpvar=$(echo "$eachfqdn" | cut -d"." -f1)
    541 			if [[ -z $tmpvar ]]; then
    542 				fullhost="$hostname$eachfqdn"
    543 			else
    544 				fullhost="$hostname.$eachfqdn"
    545 			fi
    546 
    547 			ping_check $fullhost $(gettext "System")
    548 			if [[ $fullhost == $client_machine ]]; then
    549 				:
    550 			else
    551 				fqdnlist="$fqdnlist $fullhost"
    552 			fi
    553 
    554 			if [[ $list == *,* ]]; then
    555 				((count = count + 1))
    556 				eachfqdn=$(echo "$list" | cut -d"," -f$count)
    557 			else
    558 				break
    559 			fi
    560 		done
    561 	fi
    562 }
    563 
    564 function setup_keytab {
    565 	typeset cname ask_fqdns current_release
    566 
    567 	#
    568 	# 1. kinit with ADMIN_PRINC
    569 	#
    570 
    571 	if [[ -z $ADMIN_PRINC ]]; then
    572 		printf "\n$(gettext "Enter the krb5 administrative principal to be used"): "
    573 		read ADMIN_PRINC
    574 		checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
    575 	fi
    576 
    577 	echo "$ADMIN_PRINC">$TMP_FILE
    578 
    579 	[[ -n $msad ]] && return
    580 	if egrep -s '\/admin' $TMP_FILE; then
    581 		# Already in "/admin" format, do nothing
    582 		:
    583 	else
    584 		if egrep -s '\/' $TMP_FILE; then
    585 			printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n" >&2
    586 			error_message
    587 		else
    588 			ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin")
    589 		fi
    590 	fi
    591 
    592 	printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC
    593 
    594 	cname=$(canon_resolve $KDC)
    595 	if [[ -n $cname ]]; then
    596 		kinit -S kadmin/$cname $ADMIN_PRINC
    597 	else
    598 		kinit -S kadmin/$FKDC $ADMIN_PRINC
    599 	fi
    600 	klist 1>$TMP_FILE 2>&1
    601 	if egrep -s "$(gettext "Valid starting")" $TMP_FILE && egrep -s "kadmin/$FKDC@$realm" $TMP_FILE; then
    602     		:
    603 	else
    604 		printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC >&2
    605 		error_message
    606 	fi
    607 
    608 	#
    609 	# 2. Do we want to create and/or add service principal(s) for fqdn's
    610 	#    other than the one listed in resolv.conf(4) ?
    611 	#
    612 	if [[ -z $options ]]; then
    613 		query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?"
    614 		ask_fqdns=$answer
    615 		if [[ $ask_fqdns == yes ]]; then
    616 			printf "$(gettext "Enter a comma-separated list of DNS domain names"): "
    617 			read fqdnlist
    618 			verify_fqdnlist "$fqdnlist"
    619 		else
    620 			fqdnlist=$client_machine
    621 		fi
    622 	else
    623 		if [[ -z $fqdnlist ]]; then
    624 			fqdnlist=$client_machine
    625 		fi
    626 	fi
    627 
    628 	if [[ $add_nfs == yes ]]; then
    629 		echo; call_kadmin nfs
    630 	fi
    631 
    632 	# Add the host entry to the keytab
    633 	echo; call_kadmin host
    634 
    635 }
    636 
    637 function setup_lhn {
    638 	typeset -l logical_hn
    639 
    640 	echo "$logical_hn" > $TMP_FILE
    641 	if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then
    642 		# do nothing, logical_hn is in fqdn format
    643 		:
    644 	else
    645 		if egrep -s '\.+' $TMP_FILE; then
    646 			printf "\n$(gettext "Improper format of logical hostname, exiting").\n" >&2
    647 			error_message
    648 		else
    649 			# Attach fqdn to logical_hn, to get the Fully Qualified
    650 			# Host Name of the client requested
    651 			logical_hn=$(echo "$logical_hn.$fqdn")
    652 		fi
    653 	fi
    654 
    655 	client_machine=$logical_hn
    656 
    657 	ping_check $client_machine $(gettext "System")
    658 }
    659 
    660 function usage {
    661 	printf "\n$(gettext "Usage: kclient [ options ]")\n" >&2
    662 	printf "\t$(gettext "where options are any of the following")\n\n" >&2
    663 	printf "\t$(gettext "[ -D domain_list ]  configure a client that has mul
    664 tiple mappings of doamin and/or hosts to the default realm")\n" >&2
    665 	printf "\t$(gettext "[ -K ]  configure a client that does not have host/service keys")\n" >&2
    666 	printf "\t$(gettext "[ -R realm ]  specifies the realm to use")\n" >&2
    667 	printf "\t$(gettext "[ -T kdc_vendor ]  specifies which KDC vendor is the server")\n" >&2
    668 	printf "\t$(gettext "[ -a adminuser ]  specifies the Kerberos administrator")\n" >&2
    669 	printf "\t$(gettext "[ -c filepath ]  specifies the krb5.conf path used to configure this client")\n" >&2
    670 	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
    671 	printf "\t$(gettext "[ -f fqdn_list ]  specifies which domains to configure host keys for this client")\n" >&2
    672 	printf "\t$(gettext "[ -h logicalhostname ]  configure the logical host name for a client that is in a cluster")\n" >&2
    673 	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
    674 	printf "\t$(gettext "[ -m master ]  master KDC server host name")\n" >&2
    675 	printf "\t$(gettext "[ -n ]  configure client to be an NFS client")\n" >&2
    676 	printf "\t$(gettext "[ -p profile ]  specifies which profile file to use to configure this client")\n" >&2
    677 	printf "\t$(gettext "[ -s pam_list ]  update the service for Kerberos authentication")\n" >&2
    678 	error_message
    679 }
    680 
    681 function discover_domain {
    682 	typeset dom DOMs
    683 
    684 	if [[ -z $realm ]]; then
    685 		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs S`
    686 	else
    687 		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs.$realm S`
    688 	fi
    689 
    690 	[[ -z ${DOMs[0]} ]] && return 1
    691 
    692 	dom=${DOMs[0]}
    693 
    694 	dom=${dom#*.}
    695 	dom=${dom% *}
    696 
    697 	domain=$dom
    698 
    699 	return 0
    700 }
    701 
    702 function check_nss_hosts_or_ipnodes_config {
    703 	typeset backend
    704 
    705 	for backend in $1
    706 	do
    707 		[[ $backend == dns ]] && return 0
    708 	done
    709 	return 1
    710 }
    711 
    712 function check_nss_conf {
    713 	typeset i j hosts_config
    714 
    715 	for i in hosts ipnodes
    716 	do
    717 		grep "^${i}:" /etc/nsswitch.conf|read j hosts_config
    718 		check_nss_hosts_or_ipnodes_config "$hosts_config" || return 1
    719 	done
    720 
    721 	return 0
    722 }
    723 
    724 function canon_resolve {
    725 	typeset name ip
    726 
    727 	name=`$KLOOKUP $1 C`
    728 	[[ -z $name ]] && name=`$KLOOKUP $1 A`
    729 	[[ -z $name ]] && return
    730 
    731 	ip=`$KLOOKUP $name I`
    732 	[[ -z $ip ]] && return
    733 	for i in $ip
    734 	do
    735 		if ping $i 2 > /dev/null 2>&1; then
    736 			break
    737 		else
    738 			i=
    739 		fi
    740 	done
    741 
    742 	cname=`$KLOOKUP $ip P`
    743 	[[ -z $cname ]] && return
    744 
    745 	print -- "$cname"
    746 }
    747 
    748 function rev_resolve {
    749 	typeset name ip
    750 
    751 	ip=`$KLOOKUP $1 I`
    752 
    753 	[[ -z $ip ]] && return
    754 	name=`$KLOOKUP $ip P`
    755 	[[ -z $name ]] && return
    756 
    757 	print -- $name
    758 }
    759 
    760 # Convert an AD-style domain DN to a DNS domainname
    761 function dn2dns {
    762 	typeset OIFS dname dn comp components
    763 
    764 	dn=$1
    765 	dname=
    766 
    767 	OIFS="$IFS"
    768 	IFS=,
    769 	set -A components -- $1
    770 	IFS="$OIFS"
    771 
    772 	for comp in "${components[@]}"
    773 	do
    774 		[[ "$comp" == [dD][cC]=* ]] || continue
    775 		dname="$dname.${comp#??=}"
    776 	done
    777 
    778 	print ${dname#.}
    779 }
    780 
    781 # Form a base DN from a DNS domainname and container
    782 function getBaseDN {
    783 	if [[ -n "$2" ]]
    784 	then
    785 		baseDN="CN=$1,$(dns2dn $2)"
    786 	else
    787 		baseDN="$(dns2dn $2)"
    788 	fi
    789 }
    790 
    791 # Convert a DNS domainname to an AD-style DN for that domain
    792 function dns2dn {
    793 	typeset OIFS dn labels
    794 
    795 	OIFS="$IFS"
    796 	IFS=.
    797 	set -A labels -- $1
    798 	IFS="$OIFS"
    799 
    800 	dn=
    801 	for label in "${labels[@]}"
    802 	do
    803 		dn="${dn},DC=$label"
    804 	done
    805 
    806 	print -- "${dn#,}"
    807 }
    808 
    809 function getSRVs {
    810 	typeset srv port
    811 
    812 	$KLOOKUP $1 S | while read srv port
    813 	do
    814 		if ping $srv 2 > /dev/null 2>&1; then
    815 			print -- $srv $port
    816 		fi
    817 	done
    818 }
    819 
    820 function getKDC {
    821 	typeset j
    822 
    823 	set -A KPWs -- $(getSRVs _kpasswd._tcp.$dom.)
    824 	kpasswd=${KPWs[0]}
    825 
    826 	if [[ -n $siteName ]]
    827 	then
    828 		set -A KDCs -- $(getSRVs _kerberos._tcp.$siteName._sites.$dom.)
    829 		kdc=${KDCs[0]}
    830 		[[ -n $kdc ]] && return
    831 	fi
    832 
    833 	# No site name
    834 	set -A KDCs -- $(getSRVs _kerberos._tcp.$dom.)
    835 	kdc=${KDCs[0]}
    836 	[[ -n $kdc ]] && return
    837 
    838 	# Default
    839 	set -A KDCs -- $DomainDnsZones 88
    840 	kdc=$ForestDnsZones
    841 }
    842 
    843 function getDC {
    844 	typeset j
    845 
    846 	if [[ -n $siteName ]]
    847 	then
    848 		set -A DCs -- $(getSRVs _ldap._tcp.$siteName._sites.dc._msdcs.$dom.)
    849 		dc=${DCs[0]}
    850 		[[ -n $dc ]] && return
    851 	fi
    852 
    853 	# No site name
    854 	set -A DCs -- $(getSRVs _ldap._tcp.dc._msdcs.$dom.)
    855 	dc=${DCs[0]}
    856 	[[ -n $dc ]] && return
    857 
    858 	# Default
    859 	set -A DCs -- $DomainDnsZones 389
    860 	dc=$DomainDnsZones
    861 }
    862 
    863 function write_ads_krb5conf {
    864 	printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE
    865 
    866 	exec 3>$KRB5_CONFIG
    867 	if [[ $? -ne 0 ]]; then
    868 		printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2
    869 		error_message
    870 	fi
    871 
    872 	printf "[libdefaults]\n" 1>&3
    873 	printf "\tdefault_realm = $realm\n" 1>&3
    874 	printf "\n[realms]\n" 1>&3
    875 	printf "\t$realm = {\n" 1>&3
    876 	for i in ${KDCs[@]}
    877 	do
    878 		[[ $i == +([0-9]) ]] && continue
    879 		printf "\t\tkdc = $i\n" 1>&3
    880 	done
    881 	# Defining the same as admin_server.  This would cause auth failures
    882 	# if this was different.
    883 	printf "\n\t\tkpasswd_server = $KDC\n" 1>&3
    884 	printf "\n\t\tadmin_server = $KDC\n" 1>&3
    885 	printf "\t\tkpasswd_protocol = SET_CHANGE\n\t}\n" 1>&3
    886 	printf "\n[domain_realm]\n" 1>&3
    887 	printf "\t.$dom = $realm\n\n" 1>&3
    888 	printf "[logging]\n" 1>&3
    889 	printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3
    890 	printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3
    891 	printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3
    892 	printf "[appdefaults]\n" 1>&3
    893 	printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n\t}\n" 1>&3
    894 }
    895 
    896 function getForestName {
    897 	ldapsearch -R -T -h $dc $ldap_args \
    898 	    -b "" -s base "" schemaNamingContext| \
    899 		grep ^schemaNamingContext|read j schemaNamingContext
    900 
    901 	if [[ $? -ne 0 ]]; then
    902 		printf "$(gettext "Can't find forest").\n" >&2
    903 		error_message
    904 	fi
    905 	schemaNamingContext=${schemaNamingContext#CN=Schema,CN=Configuration,}
    906 
    907 	[[ -z $schemaNamingContext ]] && return 1
    908 
    909 	forest=
    910 	while [[ -n $schemaNamingContext ]]
    911 	do
    912 		schemaNamingContext=${schemaNamingContext#DC=}
    913 		forest=${forest}.${schemaNamingContext%%,*}
    914 		[[ "$schemaNamingContext" = *,* ]] || break
    915 		schemaNamingContext=${schemaNamingContext#*,}
    916 	done
    917 	forest=${forest#.}
    918 }
    919 
    920 function getGC {
    921 	typeset j
    922 
    923 	[[ -n $gc ]] && return 0
    924 
    925 	if [[ -n $siteName ]]
    926 	then
    927 		set -A GCs -- $(getSRVs _ldap._tcp.$siteName._sites.gc._msdcs.$forest.)
    928 		gc=${GCs[0]}
    929 		[[ -n $gc ]] && return
    930 	fi
    931 
    932 	# No site name
    933 	set -A GCs -- $(getSRVs _ldap._tcp.gc._msdcs.$forest.)
    934 	gc=${GCs[0]}
    935 	[[ -n $gc ]] && return
    936 
    937 	# Default
    938 	set -A GCs -- $ForestDnsZones 3268
    939 	gc=$ForestDnsZones
    940 }
    941 
    942 #
    943 # The local variables used to calculate the IP address are of type unsigned
    944 # integer (-ui), as this is required to restrict the integer to 32b.
    945 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
    946 #
    947 function ipAddr2num {
    948 	typeset OIFS
    949 	typeset -ui16 num
    950 
    951 	if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]]
    952 	then
    953 		print 0
    954 		return 0
    955 	fi
    956 
    957 	OIFS="$IFS"
    958 	IFS=.
    959 	set -- $1
    960 	IFS="$OIFS"
    961 
    962 	num=$((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4}))
    963 
    964 	print -- $num
    965 }
    966 
    967 #
    968 # The local variables used to calculate the IP address are of type unsigned
    969 # integer (-ui), as this is required to restrict the integer to 32b.
    970 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
    971 #
    972 function num2ipAddr {
    973 	typeset -ui16 num
    974 	typeset -ui10 a b c d
    975 
    976 	num=$1
    977 	a=$((num>>24        ))
    978 	b=$((num>>16 & 16#ff))
    979 	c=$((num>>8  & 16#ff))
    980 	d=$((num     & 16#ff))
    981 	print -- $a.$b.$c.$d
    982 }
    983 
    984 #
    985 # The local variables used to calculate the IP address are of type unsigned
    986 # integer (-ui), as this is required to restrict the integer to 32b.
    987 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
    988 #
    989 function netmask2length {
    990 	typeset -ui16 netmask
    991 	typeset -i len
    992 
    993 	netmask=$1
    994 	len=32
    995 	while [[ $((netmask % 2)) -eq 0 ]]
    996 	do
    997 		netmask=$((netmask>>1))
    998 		len=$((len - 1))
    999 	done
   1000 	print $len
   1001 }
   1002 
   1003 #
   1004 # The local variables used to calculate the IP address are of type unsigned
   1005 # integer (-ui), as this is required to restrict the integer to 32b.
   1006 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
   1007 #
   1008 function getSubnets {
   1009 	typeset -ui16 addr netmask
   1010 	typeset -ui16 classa=16\#ff000000
   1011 
   1012 	ifconfig -a|while read line
   1013 	do
   1014 		addr=0
   1015 		netmask=0
   1016 		set -- $line
   1017 		[[ $1 == inet ]] || continue
   1018 		while [[ $# -gt 0 ]]
   1019 		do
   1020 			case "$1" in
   1021 				inet) addr=$(ipAddr2num $2); shift;;
   1022 				netmask) eval netmask=16\#$2; shift;;
   1023 				*) :;
   1024 			esac
   1025 			shift
   1026 		done
   1027 
   1028 		[[ $addr -eq 0 || $netmask -eq 0 ]] && continue
   1029 		[[ $((addr & classa)) -eq 16\#7f000000 ]] && continue
   1030 
   1031 		print $(num2ipAddr $((addr & netmask)))/$(netmask2length $netmask)
   1032 	done
   1033 }
   1034 
   1035 function getSite {
   1036 	typeset subnet siteDN j ldapsrv subnet_dom
   1037 
   1038 	eval "[[ -n \"\$siteName\" ]]" && return
   1039 	for subnet in $(getSubnets)
   1040 	do
   1041 		ldapsearch -R -T -h $dc $ldap_args \
   1042 		    -p 3268 -b "" -s sub cn=$subnet dn |grep ^dn|read j subnetDN
   1043 
   1044 		[[ -z $subnetDN ]] && continue
   1045 		subnet_dom=$(dn2dns $subnetDN)
   1046 		ldapsrv=$(canon_resolve DomainDnsZones.$subnet_dom)
   1047 		[[ -z $ldapsrv ]] && continue
   1048 		ldapsearch -R -T -h $ldapsrv $ldap_args \
   1049 		    -b "$subnetDN" -s base "" siteObject \
   1050 		    |grep ^siteObject|read j siteDN
   1051 
   1052 		[[ -z $siteDN ]] && continue
   1053 
   1054 		eval siteName=${siteDN%%,*}
   1055 		eval siteName=\${siteName#CN=}
   1056 		return
   1057 	done
   1058 }
   1059 
   1060 function doKRB5config {
   1061 	[[ -f $KRB5_CONFIG_FILE ]] && \
   1062 		cp $KRB5_CONFIG_FILE ${KRB5_CONFIG_FILE}-pre-kclient
   1063 
   1064 	[[ -f $KRB5_KEYTAB_FILE ]] && \
   1065 		cp $KRB5_KEYTAB_FILE ${KRB5_KEYTAB_FILE}-pre-kclient
   1066 
   1067 	[[ -s $KRB5_CONFIG ]] && cp $KRB5_CONFIG $KRB5_CONFIG_FILE
   1068 	[[ -s $KRB5_CONFIG_FILE ]] && chmod 0644 $KRB5_CONFIG_FILE
   1069 	[[ -s $new_keytab ]] && cp $new_keytab $KRB5_KEYTAB_FILE
   1070 	[[ -s $KRB5_KEYTAB_FILE ]] && chmod 0600 $KRB5_KEYTAB_FILE
   1071 }
   1072 
   1073 function addDNSRR {
   1074 	smbFMRI=svc:/network/smb/server:default
   1075 	ddnsProp=smbd/ddns_enable
   1076 	enProp=general/enabled
   1077 
   1078 	enabled=`svcprop -p $enProp $smbFMRI`
   1079 	ddns_enable=`svcprop -p $ddnsProp $smbFMRI`
   1080 
   1081 	if [[ $enabled == true && $ddns_enable != true ]]; then
   1082 		printf "$(gettext "Warning: won't create DNS records for client").\n"
   1083 		printf "$(gettext "%s property not set to 'true' for the %s FMRI").\n" $ddnsProp $smbFMRI
   1084 		return
   1085 	fi
   1086 	
   1087 	# Destroy any existing ccache as GSS_C_NO_CREDENTIAL will pick up any
   1088 	# residual default credential in the cache.
   1089 	kdestroy > /dev/null 2>&1
   1090 
   1091 	$KDYNDNS -d $1 > /dev/null 2>&1
   1092 	if [[ $? -ne 0 ]]; then
   1093 		#
   1094 		# Non-fatal, we should carry-on as clients may resolve to
   1095 		# different servers and the client could already exist there.
   1096 		#
   1097 		printf "$(gettext "Warning: unable to create DNS records for client").\n"
   1098 		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
   1099 	fi
   1100 }
   1101 
   1102 function setSMB {
   1103 	typeset domain=$1
   1104 	typeset server=$2
   1105 	smbFMRI=svc:/network/smb/server
   1106 
   1107 	printf "%s" $newpw | $KSMB -d $domain -s $server
   1108 	if [[ $? -ne 0 ]]; then
   1109 		printf "$(gettext "Warning: unable to set %s domain, server and password information").\n" $smbFMRI
   1110 		return
   1111 	fi
   1112 
   1113 	svcadm restart $smbFMRI > /dev/null 2>&1
   1114 	if [[ $? -ne 0 ]]; then
   1115 		printf "$(gettext "Warning: unable to restart %s").\n" $smbFMRI
   1116 	fi
   1117 }
   1118 
   1119 function compareDomains {
   1120 	typeset oldDom hspn newDom=$1
   1121 
   1122 	# If the client has been previously configured in a different
   1123 	# realm/domain then we need to prompt the user to see if they wish to
   1124 	# switch domains.
   1125 	klist -k 2>&1 | grep @ | read j hspn
   1126 	[[ -z $hspn ]] && return
   1127 
   1128 	oldDom=${hspn#*@}
   1129 	if [[ $oldDom != $newDom ]]; then
   1130 		printf "$(gettext "The client is currently configured in a different domain").\n"
   1131 		printf "$(gettext "Currently in the '%s' domain, trying to join the '%s' domain").\n" $oldDom $newDom
   1132 		query "$(gettext "Do you want the client to join a new domain") ?"
   1133 		printf "\n"
   1134 		if [[ $answer != yes ]]; then
   1135 			printf "$(gettext "Client will not be joined to the new domain").\n" >&2
   1136 			error_message
   1137 		fi
   1138 	fi
   1139 }
   1140 
   1141 function getKDCDC {
   1142 
   1143 	getKDC
   1144 	if [[ -n $kdc ]]; then
   1145 		KDC=$kdc
   1146 		dc=$kdc
   1147 	else
   1148 		getDC
   1149 		if [[ -n $dc ]]; then
   1150 			KDC=$dc
   1151 		else
   1152 			printf "$(gettext "Could not find domain controller server for '%s'.  Exiting").\n" $realm >&2
   1153 			error_message
   1154 		fi
   1155 	fi
   1156 }
   1157 
   1158 function join_domain {
   1159 	typeset -u upcase_nodename
   1160 	typeset -l locase_nodename
   1161 	typeset -L15 string15
   1162 	typeset netbios_nodename fqdn
   1163 	
   1164 	container=Computers
   1165 	ldap_args="-o authzid= -o mech=gssapi"
   1166 	userAccountControlBASE=4096
   1167 
   1168 	if [[ -z $ADMIN_PRINC ]]; then
   1169 		cprinc=Administrator
   1170 	else
   1171 		cprinc=$ADMIN_PRINC
   1172 	fi
   1173 
   1174 	if ! discover_domain; then
   1175 		printf "$(gettext "Can not find realm") '%s'.\n" $realm >&2
   1176 		error_message
   1177 	fi
   1178 
   1179 	dom=$domain
   1180 	realm=$domain
   1181 
   1182 	if [[ ${#hostname} -gt 15 ]]; then
   1183 		string15=$hostname
   1184 		upcase_nodename=$string15
   1185 		locase_nodename=$string15
   1186 	else
   1187 		upcase_nodename=$hostname
   1188 		locase_nodename=$hostname
   1189 	fi
   1190 
   1191 	netbios_nodename="${upcase_nodename}\$"
   1192 	fqdn=$hostname.$domain
   1193 	upn=host/${fqdn}@${realm}
   1194 
   1195 	grep=/usr/xpg4/bin/grep
   1196 
   1197 	object=$(mktemp -q -t kclient-computer-object.XXXXXX)
   1198 	if [[ -z $object ]]; then
   1199 		printf "\n$(gettext "Can not create temporary file, exiting").\n
   1200 " >&2
   1201 		error_message
   1202         fi
   1203 
   1204 	grep=/usr/xpg4/bin/grep
   1205 
   1206 	modify_existing=false
   1207 	recreate=false
   1208 
   1209 	DomainDnsZones=$(rev_resolve DomainDnsZones.$dom.)
   1210 	ForestDnsZones=$(rev_resolve ForestDnsZones.$dom.)
   1211 
   1212 	getBaseDN "$container" "$dom"
   1213 
   1214 	if [[ -n $KDC ]]; then
   1215 		dc=$KDC
   1216 	else
   1217 		getKDCDC
   1218 	fi
   1219 
   1220 	write_ads_krb5conf
   1221 
   1222 	printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm
   1223 
   1224 	kinit $cprinc@$realm
   1225 	if [[ $? -ne 0 ]]; then
   1226 		printf "$(gettext "Could not authenticate %s.  Exiting").\n" $cprinc@$realm >&2
   1227 		error_message
   1228 	fi
   1229 
   1230 	if getForestName
   1231 	then
   1232 		printf "\n$(gettext "Forest name found: %s")\n\n" $forest
   1233 	else
   1234 		printf "\n$(gettext "Forest name not found, assuming forest is the domain name").\n"
   1235 	fi
   1236 
   1237 	getGC
   1238 	getSite
   1239 
   1240 	if [[ -z $siteName ]]
   1241 	then
   1242     		printf "$(gettext "Site name not found.  Local DCs/GCs will not be discovered").\n\n"
   1243 	else
   1244     		printf "$(gettext "Looking for _local_ KDCs, DCs and global catalog servers (SRV RRs)").\n"
   1245 		getKDCDC
   1246 		getGC
   1247 
   1248 		write_ads_krb5conf
   1249 	fi
   1250 
   1251 	if [[ ${#GCs} -eq 0 ]]; then
   1252 		printf "$(gettext "Could not find global catalogs.  Exiting").\n" >&2
   1253 		error_message
   1254 	fi
   1255 
   1256 	# Check to see if the client is transitioning between domains.
   1257 	compareDomains $realm
   1258 
   1259 	# Here we check domainFunctionality to see which release:
   1260 	# 0, 1, 2: Windows 2000, 2003 Interim, 2003 respecitively
   1261 	# 3: Windows 2008
   1262 	level=0
   1263 	ldapsearch -R -T -h "$dc" $ldap_args -b "" -s base "" \
   1264 	 domainControllerFunctionality| grep ^domainControllerFunctionality| \
   1265 	 read j level
   1266 	if [[ $? -ne 0 ]]; then
   1267 		printf "$(gettext "Search for domain functionality failed, exiting").\n" >&2
   1268 		error_message
   1269 	fi
   1270 
   1271 	if ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
   1272 	    -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1
   1273 	then
   1274 		:
   1275 	else
   1276 		printf "$(gettext "Search for node failed, exiting").\n" >&2
   1277 		error_message
   1278 	fi
   1279 	ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" -s sub \
   1280 	    sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn
   1281 
   1282 	if [[ -z $dn ]]; then
   1283 		: # modify_existing is already false, which is what we want.
   1284 	else
   1285 		printf "$(gettext "Computer account '%s' already exists in the '%s' domain").\n" $upcase_nodename $realm
   1286 		query "$(gettext "Do you wish to recreate this computer account") ?"
   1287 		printf "\n"
   1288 		if [[ $answer == yes ]]; then
   1289 			recreate=true
   1290 		else
   1291 			modify_existing=true
   1292 		fi
   1293 	fi
   1294 
   1295 	if [[ $modify_existing == false && -n $dn ]]; then
   1296 		query "$(gettext "Would you like to delete any sub-object found for this computer account") ?"
   1297 		if [[ $answer == yes ]]; then
   1298 			printf "$(gettext "Looking to see if the machine account contains other objects")...\n"
   1299 			ldapsearch -R -T -h "$dc" $ldap_args -b "$dn" -s sub "" dn | while read j sub_dn
   1300 			do
   1301 				[[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue
   1302 				if $recreate; then
   1303 					printf "$(gettext "Deleting the following object: %s")\n" ${sub_dn#$dn}
   1304 					ldapdelete -h "$dc" $ldap_args "$sub_dn" > /dev/null 2>&1
   1305 					if [[ $? -ne 0 ]]; then
   1306 						printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn}
   1307 					fi
   1308 				else
   1309 					printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn}
   1310 				fi
   1311 			done
   1312 		fi
   1313 
   1314 		if $recreate; then
   1315 			ldapdelete -h "$dc" $ldap_args "$dn" > /dev/null 2>&1
   1316 			if [[ $? -ne 0 ]]; then
   1317 				printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} >&2
   1318 				error_message
   1319 			fi
   1320 		elif $modify_existing; then
   1321 			: # Nothing to delete
   1322 		else
   1323 			printf "$(gettext "A machine account already exists").\n" >&2
   1324 			error_message
   1325 		fi
   1326 	fi
   1327 
   1328 	[[ -z $dn ]] && dn="CN=${upcase_nodename},${baseDN}"
   1329 	if $modify_existing; then
   1330 		cat > "$object" <<EOF
   1331 dn: $dn
   1332 changetype: modify
   1333 replace: userPrincipalName
   1334 userPrincipalName: $upn
   1335 -
   1336 replace: servicePrincipalName
   1337 servicePrincipalName: host/${fqdn}
   1338 -
   1339 replace: userAccountControl
   1340 userAccountControl: $((userAccountControlBASE + 32 + 2))
   1341 -
   1342 replace: dNSHostname
   1343 dNSHostname: ${fqdn}
   1344 EOF
   1345 
   1346 		printf "$(gettext "A machine account already exists; updating it").\n"
   1347 		ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
   1348 		if [[ $? -ne 0 ]]; then
   1349 			printf "$(gettext "Failed to modify the AD object via LDAP").\n" >&2
   1350 			error_message
   1351 		fi
   1352 	else
   1353 		dn="CN=${upcase_nodename},${baseDN}"
   1354 		cat > "$object" <<EOF
   1355 dn: $dn
   1356 objectClass: computer
   1357 cn: $upcase_nodename
   1358 sAMAccountName: ${netbios_nodename}
   1359 userPrincipalName: $upn
   1360 servicePrincipalName: host/${fqdn}
   1361 userAccountControl: $((userAccountControlBASE + 32 + 2))
   1362 dNSHostname: ${fqdn}
   1363 EOF
   1364 
   1365 		printf "$(gettext "Creating the machine account in AD via LDAP").\n\n"
   1366 
   1367 		ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
   1368 		if [[ $? -ne 0 ]]; then
   1369 			printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2
   1370 			error_message
   1371 		fi
   1372 	fi
   1373 
   1374 	# Generate a new password for the new account
   1375 	MAX_PASS=32
   1376         i=0
   1377 
   1378 	while :
   1379 	do
   1380 		while ((MAX_PASS > i))
   1381 		do
   1382 			# 94 elements in the printable character set starting
   1383 			# at decimal 33, contiguous.
   1384 			dig=$((RANDOM%94+33))
   1385 			c=$(printf "\\`printf %o $dig`\n")
   1386 			p=$p$c
   1387 			((i+=1))
   1388 		done
   1389 
   1390 		# Ensure that we have four character classes.
   1391 		d=${p%[[:digit:]]*}
   1392 		a=${p%[[:lower:]]*}
   1393 		A=${p%[[:upper:]]*}
   1394 		x=${p%[[:punct:]]*}
   1395 
   1396 		# Just compare the number of characters from what was previously
   1397 		# matched.  If there is a difference then we found a match.
   1398 		n=${#p}
   1399 		[[ ${#d} -ne $n && ${#a} -ne $n && \
   1400 		   ${#A} -ne $n && ${#x} -ne $n ]] && break
   1401 		i=0
   1402 		p=
   1403 	done
   1404 	newpw=$p
   1405 
   1406 	# Set the new password
   1407 	printf "%s" $newpw | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1
   1408 	if [[ $? -ne 0 ]]
   1409 	then
   1410 		printf "$(gettext "Failed to set account password").\n" >&2
   1411 		error_message
   1412 	fi
   1413 
   1414 	# Lookup the new principal's kvno:
   1415 	ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
   1416 		 -s sub cn=$upcase_nodename msDS-KeyVersionNumber| \
   1417 		grep "^msDS-KeyVersionNumber"|read j kvno
   1418 	[[ -z $kvno ]] && kvno=1
   1419 
   1420 	# Set supported enctypes.  This only works for Longhorn/Vista, so we
   1421 	# ignore errors here.
   1422 	userAccountControl=$((userAccountControlBASE + 524288 + 65536))
   1423 	set -A enctypes --
   1424 
   1425 	# Do we have local support for AES?
   1426 	encrypt -l|grep ^aes|read j minkeysize maxkeysize
   1427 	val=
   1428 	if [[ $maxkeysize -eq 256 ]]; then
   1429 		val=16
   1430 		enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96
   1431 	fi
   1432 	if [[ $minkeysize -eq 128 ]]; then
   1433 		((val=val+8))
   1434 		enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96
   1435 	fi
   1436 
   1437 	# RC4 comes next (whether it's better than 1DES or not -- AD prefers it)
   1438 	if encrypt -l|$grep -q ^arcfour
   1439 	then
   1440 		((val=val+4))
   1441 		enctypes[${#enctypes[@]}]=arcfour-hmac-md5
   1442 	else
   1443 		# Use 1DES ONLY if we don't have arcfour
   1444 		userAccountControl=$((userAccountControl + 2097152))
   1445 	fi
   1446 	if encrypt -l | $grep -q ^des
   1447 	then
   1448 		((val=val+2))
   1449 		enctypes[${#enctypes[@]}]=des-cbc-md5
   1450 	fi
   1451 
   1452 	if [[ ${#enctypes[@]} -eq 0 ]]
   1453 	then
   1454 		printf "$(gettext "No enctypes are supported").\n"
   1455 		printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(1M)").\n" >&2
   1456 		error_message
   1457 	fi
   1458 
   1459 	# If domain crontroller is Longhorn or above then set new supported
   1460 	# encryption type attributes.
   1461 	if [[ $level -gt 2 ]]; then
   1462 		cat > "$object" <<EOF
   1463 dn: $dn
   1464 changetype: modify
   1465 replace: msDS-SupportedEncryptionTypes
   1466 msDS-SupportedEncryptionTypes: $val
   1467 EOF
   1468 		ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
   1469 		if [[ $? -ne 0 ]]; then
   1470 			printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n"
   1471 		fi
   1472 	fi
   1473 
   1474 	# We should probably check whether arcfour is available, and if not,
   1475 	# then set the 1DES only flag, but whatever, it's not likely NOT to be
   1476 	# available on S10/Nevada!
   1477 
   1478 	# Reset userAccountControl
   1479 	#
   1480 	#  NORMAL_ACCOUNT (512) | DONT_EXPIRE_PASSWORD (65536) |
   1481 	#  TRUSTED_FOR_DELEGATION (524288)
   1482 	#
   1483 	# and possibly UseDesOnly (2097152) (see above)
   1484 	#
   1485 	cat > "$object" <<EOF
   1486 dn: $dn
   1487 changetype: modify
   1488 replace: userAccountControl
   1489 userAccountControl: $userAccountControl
   1490 EOF
   1491 	ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
   1492 	if [[ $? -ne 0 ]]; then
   1493 		printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
   1494 		error_message
   1495 	fi
   1496 
   1497 	# Setup a keytab file
   1498 	set -A args --
   1499 	for enctype in "${enctypes[@]}"
   1500 	do
   1501 		args[${#args[@]}]=-e
   1502 		args[${#args[@]}]=$enctype
   1503 	done
   1504 
   1505 	rm $new_keytab > /dev/null 2>&1
   1506 
   1507 	cat > "$object" <<EOF
   1508 dn: $dn
   1509 changetype: modify
   1510 add: servicePrincipalName
   1511 servicePrincipalName: nfs/${fqdn}
   1512 servicePrincipalName: HTTP/${fqdn}
   1513 servicePrincipalName: root/${fqdn}
   1514 servicePrincipalName: cifs/${fqdn}
   1515 servicePrincipalName: host/${upcase_nodename}
   1516 EOF
   1517 	ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
   1518 	if [[ $? -ne 0 ]]; then
   1519 		printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
   1520 		error_message
   1521 	fi
   1522 
   1523 	#
   1524 	# In Windows, unlike MIT based implementations we salt the keys with
   1525 	# the UPN, which is based on the host/string15@realm elements, not
   1526 	# with the individual SPN strings.
   1527 	#
   1528 	salt=host/${locase_nodename}.${domain}@${realm}
   1529 
   1530 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" host/${fqdn}@${realm} > /dev/null 2>&1
   1531 	if [[ $? -ne 0 ]]
   1532 	then
   1533 		printf "$(gettext "Failed to set account password").\n" >&2
   1534 		error_message
   1535 	fi
   1536 
   1537 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" nfs/${fqdn}@${realm} > /dev/null 2>&1
   1538 	if [[ $? -ne 0 ]]
   1539 	then
   1540 		printf "$(gettext "Failed to set account password").\n" >&2
   1541 		error_message
   1542 	fi
   1543 
   1544 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" HTTP/${fqdn}@${realm} > /dev/null 2>&1
   1545 	if [[ $? -ne 0 ]]
   1546 	then
   1547 		printf "$(gettext "Failed to set account password").\n" >&2
   1548 		error_message
   1549 	fi
   1550 
   1551 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" root/${fqdn}@${realm} > /dev/null 2>&1
   1552 	if [[ $? -ne 0 ]]
   1553 	then
   1554 		printf "$(gettext "Failed to set account password").\n" >&2
   1555 		error_message
   1556 	fi
   1557 
   1558 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" cifs/${fqdn}@${realm} > /dev/null 2>&1
   1559 	if [[ $? -ne 0 ]]
   1560 	then
   1561 		printf "$(gettext "Failed to set account password").\n" >&2
   1562 		error_message
   1563 	fi
   1564 
   1565 	printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" ${netbios_nodename}@${realm} > /dev/null 2>&1
   1566 	if [[ $? -ne 0 ]]
   1567 	then
   1568 		printf "$(gettext "Failed to set account password").\n" >&2
   1569 		error_message
   1570 	fi
   1571 
   1572 	doKRB5config
   1573 
   1574 	addDNSRR $dom
   1575 
   1576 	setSMB $dom $dc
   1577 
   1578 	printf -- "---------------------------------------------------\n"
   1579 	printf "$(gettext "Setup COMPLETE").\n\n"
   1580 
   1581 	kdestroy -q 1>$TMP_FILE 2>&1
   1582 	rm -f $TMP_FILE
   1583 	rm -rf $TMPDIR > /dev/null 2>&1
   1584 
   1585 	exit 0
   1586 }
   1587 
   1588 ###########################
   1589 #	Main section	  #
   1590 ###########################
   1591 #
   1592 # Set the Kerberos config file and some default strings/files
   1593 #
   1594 KRB5_CONFIG_FILE=/etc/krb5/krb5.conf
   1595 KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab
   1596 RESOLV_CONF_FILE=/etc/resolv.conf
   1597 
   1598 KLOOKUP=/usr/lib/krb5/klookup;	check_bin $KLOOKUP
   1599 KSETPW=/usr/lib/krb5/ksetpw;	check_bin $KSETPW
   1600 KSMB=/usr/lib/krb5/ksmb;	check_bin $KSMB
   1601 KDYNDNS=/usr/lib/krb5/kdyndns;	check_bin $KDYNDNS
   1602 
   1603 dns_lookup=no
   1604 ask_fqdns=no
   1605 adddns=no
   1606 no_keytab=no
   1607 checkval=""
   1608 profile=""
   1609 typeset -u realm
   1610 typeset -l hostname KDC
   1611 
   1612 export TMPDIR="/var/run/kclient"
   1613 
   1614 mkdir $TMPDIR > /dev/null 2>&1
   1615 if [[ $? -ne 0 ]]; then
   1616 	printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2
   1617 	exit 1
   1618 fi
   1619 
   1620 TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX)
   1621 export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX)
   1622 export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) 
   1623 new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) 
   1624 if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]]
   1625 then
   1626 	printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2
   1627 	exit 1
   1628 fi
   1629 
   1630 #
   1631 # If we are interrupted, cleanup after ourselves
   1632 #
   1633 trap "exiting 1" HUP INT QUIT TERM
   1634 
   1635 if [[ -d /usr/bin ]]; then
   1636 	if [[ -d /usr/sbin ]]; then
   1637 		PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
   1638 		export PATH
   1639 	else
   1640 		printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
   1641 		exit 1
   1642 	fi
   1643 else
   1644 	printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
   1645 	exit 1
   1646 fi
   1647 
   1648 printf "\n$(gettext "Starting client setup")\n\n"
   1649 printf -- "---------------------------------------------------\n"
   1650 
   1651 #
   1652 # Check for uid 0, disallow otherwise
   1653 #
   1654 id 1>$TMP_FILE 2>&1
   1655 if [[ $? -eq 0 ]]; then
   1656 	if egrep -s "uid=0\(root\)" $TMP_FILE; then
   1657 		# uid is 0, go ahead ...
   1658 		:
   1659 	else
   1660 		printf "\n$(gettext "Administrative privileges are required to run this script, exiting").\n" >&2
   1661 		error_message
   1662 	fi
   1663 else
   1664 	cat $TMP_FILE;
   1665 	printf "\n$(gettext "uid check failed, exiting").\n" >&2
   1666 	error_message
   1667 fi
   1668 
   1669 uname=$(uname -n)
   1670 hostname=${uname%%.*}
   1671 
   1672 #
   1673 # Process the command-line arguments (if any)
   1674 #
   1675 OPTIND=1
   1676 while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T: OPTIONS
   1677 do
   1678 	case $OPTIONS in
   1679 	    D) options="$options -D"
   1680 	       domain_list="$OPTARG"
   1681 	       ;;
   1682 	    K) options="$options -K"
   1683 	       no_keytab=yes
   1684 	       ;;
   1685 	    R) options="$options -R"
   1686 	       realm="$OPTARG"
   1687 	       checkval="REALM"; check_value $realm
   1688 	       ;;
   1689 	    T) options="$options -T"
   1690 	       type="$OPTARG"
   1691 	       if [[ $type == ms_ad ]]; then
   1692 		msad=yes
   1693 		adddns=yes
   1694 	       else
   1695 		non_solaris=yes
   1696 		no_keytab=yes
   1697 	       fi
   1698 	       ;;
   1699 	    a) options="$options -a"
   1700 	       ADMIN_PRINC="$OPTARG"
   1701 	       checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
   1702 	       ;;
   1703 	    c) options="$options -c"
   1704 	       filepath="$OPTARG"
   1705 	       ;;
   1706 	    d) options="$options -d"
   1707 	       dnsarg="$OPTARG"
   1708 	       checkval="DNS_OPTIONS"; check_value $dnsarg
   1709 	       ;;
   1710 	    f) options="$options -f"
   1711 	       fqdnlist="$OPTARG"
   1712  	       ;;
   1713 	    h) options="$options -h"
   1714 	       logical_hn="$OPTARG"
   1715 	       checkval="LOGICAL_HOSTNAME"; check_value $logical_hn
   1716 	       ;;
   1717 	    k) options="$options -k"
   1718 	       kdc_list="$OPTARG"
   1719 	       ;;
   1720 	    m) options="$options -m"
   1721 	       KDC="$OPTARG"
   1722 	       checkval="KDC"; check_value $KDC
   1723 	       ;;
   1724 	    n) options="$options -n"
   1725 	       add_nfs=yes
   1726 	       ;;
   1727 	    p) options="$options -p"
   1728 	       profile="$OPTARG"
   1729 	       read_profile $profile
   1730 	       ;;
   1731 	    s) options="$options -s"
   1732 	       svc_list="$OPTARG"
   1733 	       SVCs=${svc_list//,/ }
   1734  	       ;;
   1735 	    \?) usage
   1736 	       ;;
   1737 	    *) usage
   1738 	       ;;
   1739 	esac
   1740 done
   1741 
   1742 #correct argument count after options
   1743 shift `expr $OPTIND - 1`
   1744 
   1745 if [[ -z $options ]]; then
   1746 	:
   1747 else
   1748 	if [[ $# -ne 0 ]]; then
   1749 		usage
   1750 	fi
   1751 fi
   1752 
   1753 #
   1754 # Check to see if we will be a client of a MIT, Heimdal, Shishi, etc.
   1755 #
   1756 if [[ -z $options ]]; then
   1757 	query "$(gettext "Is this a client of a non-Solaris KDC") ?"
   1758 	non_solaris=$answer
   1759 	if [[ $non_solaris == yes ]]; then
   1760 		printf "$(gettext "Which type of KDC is the server"):\n"
   1761 		printf "\t$(gettext "ms_ad: Microsoft Active Directory")\n"
   1762 		printf "\t$(gettext "mit: MIT KDC server")\n"
   1763 		printf "\t$(gettext "heimdal: Heimdal KDC server")\n"
   1764 		printf "\t$(gettext "shishi: Shishi KDC server")\n"
   1765 		printf "$(gettext "Enter required KDC type"): "
   1766 		read kdctype
   1767 		if [[ $kdctype == ms_ad ]]; then
   1768 			msad=yes
   1769 		elif [[ $kdctype == mit || $kdctype == heimdal || \
   1770 		    $kdctype == shishi ]]; then
   1771 			no_keytab=yes
   1772 		else
   1773 			printf "\n$(gettext "Invalid KDC type option, valid types are ms_ad, mit, heimdal, or shishi, exiting").\n" >&2
   1774 			error_message
   1775 		fi
   1776 	fi
   1777 fi
   1778 
   1779 [[ $msad == yes ]] && join_domain
   1780 
   1781 #
   1782 # Check for /etc/resolv.conf
   1783 #
   1784 if [[ -r $RESOLV_CONF_FILE ]]; then
   1785 	client_machine=`$KLOOKUP`
   1786 
   1787 	if [[ $? -ne 0 ]]; then
   1788 		if [[ $adddns == no ]]; then
   1789 			printf "\n$(gettext "%s does not have a DNS record and is required for Kerberos setup")\n" $hostname >&2
   1790 			error_message
   1791 		fi
   1792 
   1793 	else
   1794 		#
   1795 		# If client entry already exists then do not recreate it
   1796 		#
   1797 		adddns=no
   1798 
   1799 		hostname=${client_machine%%.*}
   1800 		domain=${client_machine#*.}
   1801 	fi
   1802 
   1803 	short_fqdn=${domain#*.*}
   1804 	short_fqdn=$(echo $short_fqdn | grep "\.")
   1805 else
   1806 	#
   1807 	# /etc/resolv.conf not present, exit ...
   1808 	#
   1809 	printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE >&2
   1810 	printf "$(gettext "Refer to resolv.conf(4), exiting").\n" >&2
   1811 	error_message
   1812 fi
   1813 
   1814 check_nss_conf || printf "$(gettext "/etc/nsswitch.conf does not make use of DNS for hosts and/or ipnodes").\n"
   1815 
   1816 [[ -n $fqdnlist ]] && verify_fqdnlist "$fqdnlist"
   1817 
   1818 if [[ -z $dnsarg && (-z $options || -z $filepath) ]]; then
   1819 	query "$(gettext "Do you want to use DNS for kerberos lookups") ?"
   1820 	if [[ $answer == yes ]]; then
   1821 		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"
   1822 		printf "\n$(gettext "Enter required DNS option"): "
   1823 		read dnsarg
   1824 		checkval="DNS_OPTIONS"; check_value $dnsarg
   1825 		set_dns_value $dnsarg
   1826 	fi
   1827 else
   1828 	[[ -z $dnsarg ]] && dnsarg=none
   1829 	set_dns_value $dnsarg
   1830 fi
   1831 
   1832 if [[ -n $kdc_list ]]; then
   1833 	if [[ -z $KDC ]]; then
   1834 		for kdc in $kdc_list; do
   1835 			break
   1836 		done
   1837 		KDC="$kdc"
   1838 	fi
   1839 fi
   1840 
   1841 if [[ -z $realm ]]; then
   1842 	printf "$(gettext "Enter the Kerberos realm"): "
   1843 	read realm
   1844 	checkval="REALM"; check_value $realm
   1845 fi
   1846 if [[ -z $KDC ]]; then
   1847 	printf "$(gettext "Specify the master KDC hostname for the above realm"): "
   1848 	read KDC
   1849 	checkval="KDC"; check_value $KDC
   1850 fi
   1851 
   1852 FKDC=`$KLOOKUP $KDC`
   1853 
   1854 #
   1855 # Ping to see if the kdc is alive !
   1856 #
   1857 ping_check $FKDC "KDC"
   1858 
   1859 if [[ -z $kdc_list && (-z $options || -z $filepath) ]]; then
   1860 	query "$(gettext "Do you have any slave KDC(s)") ?"
   1861 	if [[ $answer == yes ]]; then
   1862 		printf "$(gettext "Enter a comma-separated list of slave KDC host names"): "
   1863 		read kdc_list
   1864 	fi
   1865 fi
   1866 
   1867 [[ -n $kdc_list ]] && verify_kdcs "$kdc_list"
   1868 
   1869 #
   1870 # Check to see if we will have a dynamic presence in the realm
   1871 #
   1872 if [[ -z $options ]]; then
   1873 	query "$(gettext "Will this client need service keys") ?"
   1874 	if [[ $answer == no ]]; then
   1875 		no_keytab=yes
   1876 	fi
   1877 fi
   1878 
   1879 #
   1880 # Check to see if we are configuring the client to use a logical host name
   1881 # of a cluster environment
   1882 #
   1883 if [[ -z $options ]]; then
   1884 	query "$(gettext "Is this client a member of a cluster that uses a logical host name") ?"
   1885 	if [[ $answer == yes ]]; then
   1886 		printf "$(gettext "Specify the logical hostname of the cluster"): "
   1887 		read logical_hn
   1888 		checkval="LOGICAL_HOSTNAME"; check_value $logical_hn
   1889 		setup_lhn
   1890 	fi
   1891 fi
   1892 
   1893 if [[ -n $domain_list && (-z $options || -z $filepath) ]]; then
   1894 	query "$(gettext "Do you have multiple domains/hosts to map to realm %s"
   1895 ) ?" $realm
   1896 	if [[ $answer == yes ]]; then
   1897 		printf "$(gettext "Enter a comma-separated list of domain/hosts
   1898 to map to the default realm"): "
   1899 		read domain_list
   1900 	fi
   1901 fi
   1902 [[ -n domain_list ]] && domain_list=${domain_list//,/ }
   1903 
   1904 #
   1905 # Start writing up the krb5.conf file, save the existing one
   1906 # if already present
   1907 #
   1908 writeup_krb5_conf
   1909 
   1910 #
   1911 # Is this client going to use krb-nfs?  If so then we need to at least
   1912 # uncomment the krb5* sec flavors in nfssec.conf.
   1913 #
   1914 if [[ -z $options ]]; then
   1915 	query "$(gettext "Do you plan on doing Kerberized nfs") ?"
   1916 	add_nfs=$answer
   1917 fi
   1918 
   1919 if [[ $add_nfs == yes ]]; then
   1920 	modify_nfssec_conf
   1921 
   1922 	#	
   1923 	# We also want to enable gss as we now live in a SBD world
   1924 	#
   1925 	svcadm enable svc:/network/rpc/gss:default
   1926 	[[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable gss service").\n"
   1927 fi
   1928 
   1929 if [[ -z $options ]]; then
   1930 	query "$(gettext "Do you want to update /etc/pam.conf") ?"
   1931 	if [[ $answer == yes ]]; then
   1932 		printf "$(gettext "Enter a list of PAM service names in the following format: service:{first|only|optional}[,..]"): "
   1933 		read svc_list
   1934 		SVCs=${svc_list//,/ }
   1935 	fi
   1936 fi
   1937 [[ -n $svc_list ]] && update_pam_conf
   1938 
   1939 #
   1940 # Copy over krb5.conf master copy from filepath
   1941 #
   1942 if [[ -z $options || -z $filepath ]]; then
   1943 	query "$(gettext "Do you want to copy over the master krb5.conf file") ?"
   1944 	if [[ $answer == yes ]]; then
   1945 		printf "$(gettext "Enter the pathname of the file to be copied"): "
   1946 		read filepath
   1947 	fi
   1948 fi
   1949 
   1950 if [[ -n $filepath && -r $filepath ]]; then
   1951 	cp $filepath $KRB5_CONFIG
   1952 	if [[ $? -eq 0 ]]; then
   1953 		printf "$(gettext "Copied %s to %s").\n" $filepath $KRB5_CONFIG
   1954 	else
   1955 		printf "$(gettext "Copy of %s failed, exiting").\n" $filepath >&2
   1956 		error_message
   1957 	fi
   1958 elif [[ -n $filepath ]]; then
   1959 	printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2
   1960 	error_message
   1961 fi
   1962 
   1963 doKRB5config
   1964 
   1965 #
   1966 # Populate any service keys needed for the client in the keytab file
   1967 #
   1968 if [[ $no_keytab != yes ]]; then
   1969 	setup_keytab
   1970 else
   1971 	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
   1972 	printf "$(gettext "Client will also not be able to host services that use Kerberos").\n"
   1973 fi
   1974 
   1975 printf -- "\n---------------------------------------------------\n"
   1976 printf "$(gettext "Setup COMPLETE").\n\n"
   1977 
   1978 #
   1979 # If we have configured the client in a cluster we need to remind the user
   1980 # to propagate the keytab and configuration files to the other members.
   1981 #
   1982 if [[ -n $logical_hn ]]; then
   1983 	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"
   1984 fi
   1985 
   1986 #
   1987 # Cleanup.
   1988 #
   1989 kdestroy -q 1>$TMP_FILE 2>&1
   1990 rm -f $TMP_FILE
   1991 rm -rf $TMPDIR > /dev/null 2>&1
   1992 exit 0
   1993