xref: /illumos-gate/usr/src/cmd/krb5/kadmin/kclient/kclient.sh (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1#!/bin/ksh -p
2#
3# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
4# Use is subject to license terms.
5#
6# ident	"%Z%%M%	%I%	%E% SMI"
7#
8# This script is used to setup the Kerberos client by
9# supplying information about the Kerberos realm and kdc.
10#
11# The kerberos configuration file (/etc/krb5/krb5.conf) would
12# be generated and local host's keytab file setup. The script
13# can also optionally setup the system to do kerberized nfs and
14# bringover a master krb5.conf copy from a specified location.
15
16error_message() {
17	kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1
18	rm -f $TMP_FILE
19	printf "---------------------------------------------------\n"
20	printf "$(gettext "Setup FAILED").\n\n"
21	exit 1
22}
23
24cannot_create() {
25	typeset filename="$1"
26	typeset stat="$2"
27	if [ $stat -ne 0 ]; then
28		printf "\n$(gettext "Cannot create/edit %s, exiting").\n" $filename
29		error_message
30	fi
31}
32
33modify_nfssec_conf() {
34	if [ -r $NFSSEC_FILE ]; then
35		cat $NFSSEC_FILE > $NFSSEC_FILE.sav
36		cannot_create $NFSSEC_FILE.sav $?
37	fi
38
39	cat $NFSSEC_FILE > $TMP_FILE
40	cannot_create $TMP_FILE $?
41
42	if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1; then
43		sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE
44		cannot_create $NFSSEC_FILE $?
45	fi
46}
47
48call_kadmin() {
49	typeset svc="$1"
50	typeset bool1 bool2 bool3 bool4
51
52	for listentry in $fqdnlist; do
53
54	# Reset conditional vars to 1
55	bool1=1; bool2=1; bool3=1; bool4=1
56
57	service_princ=$(echo "$svc/$listentry")
58	getprincsubcommand="getprinc $service_princ"
59	anksubcommand="addprinc -randkey $service_princ"
60	ktaddsubcommand="ktadd $service_princ"
61
62	kadmin -c $TMP_CCACHE -q "$getprincsubcommand" 1>$TMP_FILE 2>&1
63
64	egrep -s "get_principal: Principal does not exist" $TMP_FILE
65	bool1=$?
66	egrep -s "get_principal: Operation requires ``get" $TMP_FILE
67	bool2=$?
68
69	if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then
70		kadmin -c $TMP_CCACHE -q "$anksubcommand" 1>$TMP_FILE 2>&1
71
72		egrep -s "add_principal: Principal or policy already exists while creating \"$service_princ@$REALM\"." $TMP_FILE
73		bool3=$?
74
75		egrep -s "Principal \"$service_princ@$REALM\" created." $TMP_FILE
76		bool4=$?
77
78		if [[ $bool3 -eq 0 || $bool4 -eq 0 ]]; then
79			printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ
80		else
81			cat $TMP_FILE;
82			printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ
83			error_message
84		fi
85	else
86		printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ
87	fi
88
89	klist -k 1>$TMP_FILE 2>&1
90	egrep -s "$service_princ@$REALM" $TMP_FILE
91	if [ $? -eq 0 ]; then
92		printf "$(gettext "%s entry already present in keytab").\n" $service_princ
93	else
94		kadmin -c $TMP_CCACHE -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1
95		egrep -s "added to keytab WRFILE:$KRB5_KEYTAB_FILE." $TMP_FILE
96		if [ $? -ne 0 ]; then
97			cat $TMP_FILE;
98			printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ
99			error_message
100		else
101			printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ
102		fi
103	fi
104
105	done
106}
107
108writeup_krb5_conf() {
109	printf "\n$(gettext "Setting up %s").\n" $KRB5_CONFIG_FILE
110
111	if [ -r $KRB5_CONFIG_FILE ]; then
112		cat $KRB5_CONFIG_FILE > $KRB5_CONFIG_FILE.sav
113		cannot_create $KRB5_CONFIG_FILE.sav $?
114	fi
115
116	exec > $KRB5_CONFIG_FILE
117	if [ $? -ne 0 ]; then
118		exec > /dev/tty
119		printf "\n$(gettext "Cannot write to %s, exiting").\n" $KRB5_CONFIG_FILE
120		error_message
121	fi
122
123	printf "[libdefaults]\n"
124	if [ "$dns_lookup" = yes ]; then
125	    printf "\t$dnsarg = on\n"
126	    if [ "$dnsarg" = dns_lookup_kdc ]; then
127		printf "\tdefault_realm = $REALM\n"
128		printf "\n[domain_realm]\n"
129		printf "\t$KDC = $REALM\n"
130		printf "\t$client_machine = $REALM\n"
131		printf "\t.$fqdn = $REALM\n\n"
132	    else
133		if [ "$dnsarg" = dns_lookup_realm ]; then
134
135		    printf "\n[realms]\n"
136		    printf "\t$REALM = {\n"
137		    printf "\t\tkdc = $KDC\n"
138		    printf "\t\tadmin_server = $KDC\n"
139		    printf "\t}\n\n"
140		else
141		    printf "\n\n"
142		fi
143	    fi
144	else
145	    printf "\tdefault_realm = $REALM\n\n"
146
147	    printf "[realms]\n"
148	    printf "\t$REALM = {\n"
149	    printf "\t\tkdc = $KDC\n"
150	    printf "\t\tadmin_server = $KDC\n"
151	    printf "\t}\n\n"
152
153	    printf "[domain_realm]\n"
154	    printf "\t$KDC = $REALM\n"
155	    printf "\t$client_machine = $REALM\n"
156	    printf "\t.$fqdn = $REALM\n\n"
157	fi
158
159	printf "[logging]\n"
160	printf "\tdefault = FILE:/var/krb5/kdc.log\n"
161	printf "\tkdc = FILE:/var/krb5/kdc.log\n"
162
163	#
164	# return output to TTY
165	#
166	exec > /dev/tty
167}
168
169ask() {
170	question=$1
171	default_answer=$2
172	if [ -z "$default_answer" ]; then
173		printf "$question :\c"
174	else
175		printf "$question [$default_answer]: \c"
176	fi
177	read answer
178	test -z "$answer" && answer="$default_answer"
179}
180
181yesno() {
182	typeset question="$1"
183	answer=""
184	while [ -z "$answer" ]; do
185		ask "$question" y/n
186		case "$answer" in
187			y|yes)	answer=yes;;
188			n|no)	answer=no;;
189			*)	answer="";;
190		esac
191	done
192}
193
194query() {
195	yesno "$*"
196	if [ "$answer" = no ]; then
197		printf "\t$(gettext "No action performed").\n"
198	fi
199}
200
201
202read_profile() {
203	typeset param value
204	typeset file="$1"
205	if [[ ! -d $file && -r $file ]]; then
206		while read param value
207		do
208			case "$param" in
209			REALM)  if [ -z "$REALM" ]; then
210					REALM="$value"
211					checkval="REALM"; check_value $REALM
212				fi
213				;;
214			KDC)    if [ -z "$KDC" ]; then
215					KDC="$value"
216					checkval="KDC"; check_value $KDC
217				fi
218				;;
219			ADMIN)  if [ -z "$ADMIN_PRINC" ]; then
220					ADMIN_PRINC="$value"
221					checkval="ADMIN_PRINC"
222    					check_value $ADMIN_PRINC
223				fi
224				;;
225			FILEPATH)  if [ -z "$filepath" ]; then
226					filepath="$value"
227				   fi
228				   ;;
229			NFS)    if [ -z "$add_nfs" ]; then
230				    if [ "$value" = 1 ]; then
231					    add_nfs=yes
232				    else
233					    add_nfs=no
234				    fi
235				fi
236				;;
237			DNSLOOKUP) if [ -z "$dnsarg" ]; then
238					dnsarg="$value"
239					checkval="DNS_OPTIONS"
240					check_value $dnsarg
241				   fi
242				   ;;
243			FQDN) if [ -z "$fqdnlist" ]; then
244					fqdnlist="$value"
245					checkval="FQDN"
246					check_value $fqdnlist
247					verify_fqdnlist "$fqdnlist"
248			      fi
249			      ;;
250			esac
251		done <$file
252	else
253		printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file
254		error_message
255	fi
256}
257
258ping_check() {
259	typeset machine="$1"
260	typeset string="$2"
261	if ping $machine > /dev/null; then
262		:
263	else
264		printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine
265		error_message
266	fi
267
268	# Output timesync warning if not using a profile, i.e. in
269	# interactive mode.
270	if [[ -z "$profile" && "$string" = KDC ]]; then
271		# It's difficult to sync up time with KDC esp. if in a
272		# zone so just print a warning about KDC time sync.
273		printf "\n$(gettext "Note, this system and the KDC's time must be within 5 minutes of each other for Kerberos to function.  Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n"
274	fi
275}
276
277check_value() {
278	typeset arg="$1"
279	if [ -z "$arg" ]; then
280		printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval
281		error_message
282	else
283		echo "$arg">$TMP_FILE
284		if egrep -s '[*$^#!]+' $TMP_FILE; then
285			printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval
286			error_message
287		fi
288	fi
289}
290
291set_dns_value() {
292	typeset arg="$1"
293	if [[ "$arg" = dns_lookup_kdc  ||  "$arg" = dns_lookup_realm  || "$arg" = dns_fallback ]]; then
294		dns_lookup=yes
295	else
296		arg=$(echo "$arg"|tr '[A-Z]' '[a-z]')
297		if [ "$arg" = none ]; then
298			dns_lookup=no
299		else
300			printf "\n$(gettext "Invalid DNS lookup option, exiting").\n"
301			error_message
302		fi
303	fi
304}
305
306verify_fqdnlist() {
307	integer count=1
308
309	list=$(echo "$1" | tr -d " " | tr -d "\t")
310	hostname=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1)
311	fqdnlist=$client_machine
312
313	eachfqdn=$(echo "$list" | cut -d"," -f$count)
314	if [ -z "$eachfqdn" ]; then
315		printf "\n$(gettext "If the -f option is used, atleast one FQDN should be listed").\n\n"
316
317		usage
318	else
319		while [ ! -z "$eachfqdn" ]; do
320			tmpvar=$(echo "$eachfqdn" | cut -d"." -f1)
321			if [ -z "$tmpvar" ]; then
322				fullhost="$hostname$eachfqdn"
323			else
324				fullhost="$hostname.$eachfqdn"
325			fi
326
327			ping_check $fullhost "System"
328			if [ "$fullhost" = "$client_machine" ]; then
329				:
330			else
331				fqdnlist="$fqdnlist $fullhost"
332			fi
333
334			if [[ "$list" == *,* ]]; then
335				((count = count + 1))
336				eachfqdn=$(echo "$list" | cut -d"," -f$count)
337			else
338				break
339			fi
340		done
341	fi
342}
343
344usage() {
345	printf "\n$(gettext "Usage: kclient [ -n ] [ -R realm ] [ -k kdc ] [ -a adminuser ] [ -c filepath ] [ -d dnsarg ] [ -f fqdn_list ] [ -p profile ]")\n\n"
346	printf "$(gettext "Refer kclient(1M) for details, exiting").\n"
347	error_message
348}
349
350###########################
351#	Main section	  #
352###########################
353#
354# Set the Kerberos config file and some default strings/files
355#
356KRB5_CONFIG_FILE="/etc/krb5/krb5.conf"
357KRB5_KEYTAB_FILE="/etc/krb5/krb5.keytab"
358RESOLV_CONF_FILE="/etc/resolv.conf"
359NFSSEC_FILE="/etc/nfssec.conf"
360dns_lookup="no"
361ask_fqdns="no"
362checkval=""
363profile=""
364
365# Set OS release level to Solaris 10, inorder to track the requirement
366# of the root/fqdn service principal for kerberized NFS.
367release_level=10
368
369if [ -x /usr/bin/mktemp ]; then
370	TMP_FILE=$(/usr/bin/mktemp /etc/krb5/krb5tmpfile.XXXXXX)
371	TMP_CCACHE=$(/usr/bin/mktemp /etc/krb5/krb5tmpccache.XXXXXX)
372else
373	TMP_FILE="/etc/krb5/krb5tmpfile.$$"
374	TMP_CCACHE="/etc/krb5/krb5tmpccache.$$"
375fi
376
377if [[ -z "$TMP_FILE" || -z "$TMP_CCACHE" ]]; then
378	printf "\n$(gettext "Temporary file creation failed, exiting").\n" >&2
379	exit 1
380fi
381
382#
383# If we are interrupted, cleanup after ourselves
384#
385trap "/usr/bin/rm -f $TMP_FILE $TMP_CCACHE; exit 1" HUP INT QUIT TERM
386
387if [ -d /usr/bin ]; then
388	if [ -d /usr/sbin ]; then
389		PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
390		export PATH
391	else
392		printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
393		exit 1
394	fi
395else
396	printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
397	exit 1
398fi
399
400printf "\n$(gettext "Starting client setup")\n\n"
401printf "---------------------------------------------------\n"
402
403#
404# Check for uid 0, disallow otherwise
405#
406id 1>$TMP_FILE 2>&1
407if [ $? -eq 0 ]; then
408	if egrep -s "uid=0\(root\)" $TMP_FILE; then
409		# uid is 0, go ahead ...
410		:
411	else
412		printf "\n$(gettext "Root privileges are required to run this script, exiting").\n"
413		error_message
414	fi
415else
416	cat $TMP_FILE;
417	printf "\n$(gettext "uid check failed, exiting").\n"
418	error_message
419fi
420
421#
422# Check for /etc/resolv.conf
423#
424if [ -r $RESOLV_CONF_FILE ]; then
425	while read label text
426	do
427		case "$label" in
428		domain) # Copy the entry into $fqdn
429			if [ -z "$text" ]; then
430				printf "\n$(gettext "DNS domain info malformed in %s, exiting").\n" $RESOLV_CONF_FILE
431				error_message
432			fi
433			fqdn=$(echo "$text"|tr '[A-Z]' '[a-z]')
434			break
435			;;
436		esac
437	done <$RESOLV_CONF_FILE
438
439	if [ -z "$fqdn" ]; then
440		printf "\n$(gettext "DNS domain info missing in %s, exiting").\n" $RESOLV_CONF_FILE
441		error_message
442	fi
443else
444	#
445	# /etc/resolv.conf not present, exit ...
446	#
447	printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE
448	printf "$(gettext "Refer resolv.conf(4), exiting").\n"
449	error_message
450fi
451
452client_machine=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1).$fqdn
453
454#
455# Process the command-line arguments (if any)
456#
457OPTIND=1
458while getopts np:R:k:a:c:d:f: OPTIONS
459do
460	case $OPTIONS in
461	    p) options="$options -p"
462	       profile="$OPTARG"
463	       read_profile $profile
464	       ;;
465	    R) options="$options -R"
466	       REALM="$OPTARG"
467	       checkval="REALM"; check_value $REALM
468	       ;;
469	    k) options="$options -k"
470	       KDC="$OPTARG"
471	       checkval="KDC"; check_value $KDC
472	       ;;
473	    a) options="$options -a"
474	       ADMIN_PRINC="$OPTARG"
475	       checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
476	       ;;
477	    c) options="$options -c"
478	       filepath="$OPTARG"
479	       ;;
480	    d) options="$options -d"
481	       dnsarg="$OPTARG"
482	       checkval="DNS_OPTIONS"; check_value $dnsarg
483	       ;;
484	    f) options="$options -f"
485	       fqdnlist="$OPTARG"
486	       verify_fqdnlist "$fqdnlist"
487 	       ;;
488	    n) options="$options -n"
489	       add_nfs=yes
490	       ;;
491	    \?) usage
492		;;
493	    *) usage
494	       ;;
495	esac
496done
497
498#correct argument count after options
499shift `expr $OPTIND - 1`
500
501if [ -z "$options" ]; then
502	:
503else
504	if [ $# -ne 0 ]; then
505		usage
506	fi
507fi
508
509if [ -z "$dnsarg" ]; then
510	query "$(gettext "Do you want to use DNS for kerberos lookups") ?"
511	if [ "$answer" = yes ]; then
512		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"
513		printf "\n$(gettext "Enter required DNS option"): \c"
514		read dnsarg
515		checkval="DNS_OPTIONS"; check_value $dnsarg
516		set_dns_value $dnsarg
517	fi
518else
519	set_dns_value $dnsarg
520fi
521
522if [ -z "$REALM" ]; then
523	printf "$(gettext "Enter the Kerberos realm"): \c"
524	read REALM
525	checkval="REALM"; check_value $REALM
526fi
527if [ -z "$KDC" ]; then
528	printf "$(gettext "Specify the KDC hostname for the above realm"): \c"
529	read KDC
530	checkval="KDC"; check_value $KDC
531fi
532
533REALM=$(echo "$REALM"|tr '[a-z]' '[A-Z]')
534KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]')
535
536echo "$KDC">$TMP_FILE
537if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then
538	# do nothing, KDC is in fqdn format
539	:
540	echo "$KDC"
541else
542	if egrep -s '\.+' $TMP_FILE; then
543		printf "\n$(gettext "Improper format of KDC hostname, exiting").\n"
544		error_message
545	else
546		# Attach fqdn to KDC, to get the Fully Qualified Domain Name
547		# of the KDC requested
548		KDC=$(echo "$KDC.$fqdn")
549	fi
550fi
551#
552# Ping to see if the kdc is alive !
553#
554ping_check $KDC "KDC"
555
556
557#
558# Start writing up the krb5.conf file, save the existing one
559# if already present
560#
561writeup_krb5_conf
562
563
564#
565# Done creating krb5.conf, so now we ...
566#
567# 1. kinit with ADMIN_PRINC
568#
569
570if [ -z "$ADMIN_PRINC" ]; then
571	printf "\n$(gettext "Enter the krb5 administrative principal to be used"): \c"
572	read ADMIN_PRINC
573	checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC
574fi
575
576echo "$ADMIN_PRINC">$TMP_FILE
577
578if egrep -s '\/admin' $TMP_FILE; then
579	# Already in "/admin" format, do nothing
580	:
581else
582	if egrep -s '\/' $TMP_FILE; then
583		printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n"
584		error_message
585	else
586		ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin")
587	fi
588fi
589
590printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC
591KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]')
592kinit -c $TMP_CCACHE -S kadmin/$KDC $ADMIN_PRINC
593klist -c $TMP_CCACHE 1>$TMP_FILE 2>&1
594if egrep -s "Valid starting" $TMP_FILE && egrep -s "kadmin/$KDC@$REALM" $TMP_FILE; then
595    	:
596else
597	printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC
598	error_message
599fi
600
601#
602# 2. Do we want to create and/or add service principal(s) for fqdn's
603#    other than the one listed in resolv.conf(4) ?
604#
605if [ -z "$options" ]; then
606	echo
607	query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $REALM ?"
608	ask_fqdns=$answer
609	if [ "$ask_fqdns" = yes ]; then
610		printf "$(gettext "Enter a comma-seperated list of DNS domain names"): \c"
611		read fqdnlist
612		verify_fqdnlist "$fqdnlist"
613	else
614		fqdnlist=$client_machine
615	fi
616else
617	if [ -z "$fqdnlist" ]; then
618		fqdnlist=$client_machine
619	fi
620fi
621
622#
623# 3. Set up keytab/config files for nfs/host/root entries (if requested)
624#
625echo
626if [ -z "$options" ]; then
627	query "$(gettext "Do you plan on doing Kerberized nfs") ?"
628	add_nfs=$answer
629fi
630
631if [ "$add_nfs" = yes ]; then
632	modify_nfssec_conf
633	echo; call_kadmin nfs
634	#
635	# Check to see if the system is a pre-S10 system which would
636	# require the root/FQDN svc principal for kerberized NFS.
637	#
638	current_release=$(uname -r | cut -d"." -f2)
639	if [ $current_release -lt $release_level ]; then
640		echo; call_kadmin root
641	fi
642fi
643
644# Add the host entry to the keytab
645echo; call_kadmin host
646
647#
648# 4. Copy over krb5.conf master copy from filepath
649#
650if [ -z "$options" ]; then
651	echo
652	query "$(gettext "Do you want to copy over the master krb5.conf file") ?"
653	if [ "$answer" = yes ]; then
654		printf "$(gettext "Enter the pathname of the file to be copied"): \c"
655		read filepath
656	fi
657fi
658
659if [ -z "$filepath" ]; then
660	:
661else
662	if [ -r $filepath ]; then
663		cp $filepath $KRB5_CONFIG_FILE
664		if [ $? -eq 0 ]; then
665			printf "\n$(gettext "Copied %s").\n" $filepath
666		else
667			printf "\n$(gettext "Copy of %s failed, exiting").\n" $filepath
668			error_message
669		fi
670	else
671		printf "\n$(gettext "%s not found, exiting").\n" $filepath
672		error_message
673	fi
674fi
675
676printf "\n---------------------------------------------------\n"
677printf "$(gettext "Setup COMPLETE").\n"
678
679#
680# 5. Cleanup, please !
681#
682kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1
683rm -f $TMP_FILE
684exit 0
685