diff options
| author | semery <none@none> | 2008-05-15 15:49:40 -0700 | 
|---|---|---|
| committer | semery <none@none> | 2008-05-15 15:49:40 -0700 | 
| commit | bd211b8556ef6b18ebf137419bd5555d65271664 (patch) | |
| tree | 3b74a827b21b9d3a1cd9263bc17f1739c91661da /usr/src | |
| parent | 289175a0e8fac168b0e8cfb77ab67d29bc5038d2 (diff) | |
| download | illumos-joyent-bd211b8556ef6b18ebf137419bd5555d65271664.tar.gz | |
PSARC/2007/401 kclient version 2
6263626 kclient does not accept 'search' type lines in resolv.conf
6287615 kclient enhancement to support domain joining for AD interop
6362266 kclient doesn't support aliasing KDCs
6405691 kclient should be used to configure DHCP/VPN clients and for non-Solaris KDCs
6629530 kpasswd(1) in SET_CHANGE mode should try kpasswd_server first
Diffstat (limited to 'usr/src')
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/Makefile | 87 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/kclient.sh | 1867 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/kdyndns.c | 85 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/ksetpw.c | 384 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/ksmb.c | 131 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/pam_krb5_first | 33 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/pam_krb5_only | 33 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kclient/pam_krb5_optional | 33 | ||||
| -rw-r--r-- | usr/src/cmd/krb5/kadmin/kdcmgr/klookup.c | 137 | ||||
| -rw-r--r-- | usr/src/lib/krb5/kadm5/clnt/client_init.c | 20 | ||||
| -rw-r--r-- | usr/src/lib/krb5/kadm5/kadm_host_srv_names.c | 81 | ||||
| -rw-r--r-- | usr/src/pkgdefs/SUNWkdcu/prototype_com | 9 | ||||
| -rw-r--r-- | usr/src/pkgdefs/etc/exception_list_i386 | 5 | ||||
| -rw-r--r-- | usr/src/pkgdefs/etc/exception_list_sparc | 5 | 
14 files changed, 2546 insertions, 364 deletions
| diff --git a/usr/src/cmd/krb5/kadmin/kclient/Makefile b/usr/src/cmd/krb5/kadmin/kclient/Makefile index d202c29af5..093d17592d 100644 --- a/usr/src/cmd/krb5/kadmin/kclient/Makefile +++ b/usr/src/cmd/krb5/kadmin/kclient/Makefile @@ -1,5 +1,24 @@  # -# Copyright 2004 Sun Microsystems, Inc.  All rights reserved. +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.  # Use is subject to license terms.  #  # ident	"%Z%%M%	%I%	%E% SMI" @@ -7,33 +26,75 @@  # Makefile for Kerberos client-install utility.  # -PROG=	kclient -SRCS=	kclient.sh +PROG=		ksetpw \ +		kdyndns \ +		ksmb + +SHFILES=	kclient +SECFILES=	pam_krb5_first \ +		pam_krb5_only \ +		pam_krb5_optional +CLOBBERFILES=	$(SHFILES) + +KRB5SBINSHFILES=$(SHFILES:%=$(KRB5SBIN)/%) + +USRLIBSEC=	$(ROOT)/usr/lib/security +SEC=		$(SECFILES:%=$(USRLIBSEC)/%) +$(SEC):=	FILEMODE = $(LIBFILEMODE) + +KS_OBJS=	ksetpw.o +KD_OBJS=	kdyndns.o +KSMB_OBJS=	ksmb.o + +OBJS=		$(KS_OBJS) $(KD_OBJS) $(KSMB_OBJS) + +SSRCS=	kclient.sh +SRCS=	$(OBJS:%.o=%.c)  include ../../../Makefile.cmd +include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 + +POFILE=	$(SSRCS:%.sh=%.po) -POFILE=		$(SRCS:%.sh=%.po) +CPPFLAGS += -I$(SRC)/uts/common/gssapi/include \ +	-I$(SRC)/lib/krb5 -I$(SRC)/lib/gss_mechs/mech_krb5/include \ +	-I$(SRC)/uts/common/gssapi/include  \ +	-I$(SRC)/uts/common/gssapi/mechs/krb5/include -KCLIENTPROG=	$(PROG:%=$(KRB5SBIN)/%) +LDFLAGS += $(KRUNPATH) -$(KCLIENTPROG) := FILEMODE = 555 +KSLDLIBS=	$(LDLIBS) $(KMECHLIB) +KDLDLIBS=	$(LDLIBS) -L$(ROOT)/usr/lib/smbsrv + +DYNFLAGS +=	-R/usr/lib/smbsrv  .KEEP_STATE: -all: $(PROG) +all: $(PROG) $(SHFILES) $(SEC) + +install: all $(KRB5SBIN) $(KRB5SBINSHFILES) $(KRB5LIBSHFILES) $(KRB5LIBPROG) -install: all $(KRB5SBIN) $(KCLIENTPROG) +kdyndns:	$(KD_OBJS) +	$(LINK.c) $(KD_OBJS) -o $@ $(DYNFLAGS) $(KDLDLIBS) -lsmbns +	$(POST_PROCESS) + +ksmb:		$(KSMB_OBJS) +	$(LINK.c) $(KSMB_OBJS) -o $@ $(DYNFLAGS) $(KDLDLIBS) -lsmb +	$(POST_PROCESS) + +ksetpw:		$(KS_OBJS) +	$(LINK.c) $(KS_OBJS) -o $@ $(KSLDLIBS) +	$(POST_PROCESS)  $(KRB5SBIN):  	$(INS.dir) -$(KCLIENTPROG)/%: % +$(USRLIBSEC)/%: %  	$(INS.file) -lint: -	@echo "nothing to lint" -  clean: -	$(RM) $(PROG) +	$(RM) $(PROG) $(SHFILES) + +lint:	lint_SRCS  include ../../../Makefile.targ diff --git a/usr/src/cmd/krb5/kadmin/kclient/kclient.sh b/usr/src/cmd/krb5/kadmin/kclient/kclient.sh index 4a4d030923..a5a5b64212 100644 --- a/usr/src/cmd/krb5/kadmin/kclient/kclient.sh +++ b/usr/src/cmd/krb5/kadmin/kclient/kclient.sh @@ -1,6 +1,25 @@  #!/bin/ksh -p  # -# Copyright 2004 Sun Microsystems, Inc.  All rights reserved. +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.  # Use is subject to license terms.  #  # ident	"%Z%%M%	%I%	%E% SMI" @@ -13,25 +32,87 @@  # can also optionally setup the system to do kerberized nfs and  # bringover a master krb5.conf copy from a specified location. -error_message() { -	kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1 -	rm -f $TMP_FILE -	printf "---------------------------------------------------\n" -	printf "$(gettext "Setup FAILED").\n\n" -	exit 1 +function cleanup { +	integer ret=$1 + +	kdestroy -q 1> $TMP_FILE 2>&1 +	rm -r $TMPDIR > /dev/null 2>&1 + +	exit $ret +} +function exiting { +	 +        printf "\n$(gettext "Exiting setup, nothing changed").\n\n" + +	cleanup $1 +} + +function error_message { + +        printf -- "---------------------------------------------------\n" +        printf "$(gettext "Setup FAILED").\n\n" + +        cleanup 1 +} + +function check_bin { + +	typeset bin=$1 + +	if [[ ! -x $bin ]]; then +		printf "$(gettext "Could not access/execute %s").\n" $bin +		error_message +	fi  } -cannot_create() { +function cannot_create {  	typeset filename="$1"  	typeset stat="$2" -	if [ $stat -ne 0 ]; then -		printf "\n$(gettext "Cannot create/edit %s, exiting").\n" $filename + +	if [[ $stat -ne 0 ]]; then +		printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2  		error_message  	fi  } -modify_nfssec_conf() { -	if [ -r $NFSSEC_FILE ]; then +function update_pam_conf { +	typeset PAM TPAM service + +	PAM=/etc/pam.conf + +	TPAM=$(mktemp -q -t kclient-pamconf.XXXXXX) +	if [[ -z $TPAM ]]; then +		printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2 +		error_message +	fi + +	cp $PAM $TPAM >/dev/null 2>&1 + +	printf "$(gettext "Configuring %s").\n\n" $PAM + +	for service in $SVCs; do +		svc=${service%:*} +		auth_type=${service#*:} +		if egrep -s "^$svc[ 	][ 	]*auth.*pam_krb5*" $TPAM; then +			printf "\n$(gettext "The %s service is already configure +d for pam_krb5, please merge this service in %s").\n" $svc $PAM >&2 +			continue +		else +			exec 3>>$TPAM +			printf "\n$svc\tauth include\t\tpam_krb5_$auth_type\n" 1 +>&3 +		fi +	done + +	cp $TPAM $PAM > /dev/null 2>&1 + +	rm $TPAM > /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 @@ -45,293 +126,435 @@ modify_nfssec_conf() {  	fi  } -call_kadmin() { +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") +	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 $TMP_CCACHE -q "$getprincsubcommand" 1>$TMP_FILE 2>&1 +	kadmin -c $KRB5CCNAME -q "$getprincsubcommand" 1>$TMP_FILE 2>&1 -	egrep -s "get_principal: Principal does not exist" $TMP_FILE +	egrep -s $(gettext "get_principal: Principal does not exist") $TMP_FILE  	bool1=$? -	egrep -s "get_principal: Operation requires ``get" $TMP_FILE +	egrep -s $(gettext "get_principal: Operation requires ``get") $TMP_FILE  	bool2=$?  	if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then -		kadmin -c $TMP_CCACHE -q "$anksubcommand" 1>$TMP_FILE 2>&1 +		kadmin -c $KRB5CCNAME -q "$anksubcommand" 1>$TMP_FILE 2>&1 -		egrep -s "add_principal: Principal or policy already exists while creating \"$service_princ@$REALM\"." $TMP_FILE +		egrep -s $(gettext "add_principal: Principal or policy already exists while creating \"$service_princ@$realm\".") $TMP_FILE  		bool3=$? -		egrep -s "Principal \"$service_princ@$REALM\" created." $TMP_FILE +		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 +			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 +		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 +	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 -		kadmin -c $TMP_CCACHE -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1 -		egrep -s "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 -			error_message -		else -			printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ -		fi +		printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ  	fi  	done  } -writeup_krb5_conf() { -	printf "\n$(gettext "Setting up %s").\n" $KRB5_CONFIG_FILE +function writeup_krb5_conf { +	typeset dh -	if [ -r $KRB5_CONFIG_FILE ]; then -		cat $KRB5_CONFIG_FILE > $KRB5_CONFIG_FILE.sav -		cannot_create $KRB5_CONFIG_FILE.sav $? -	fi +	printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE -	exec > $KRB5_CONFIG_FILE -	if [ $? -ne 0 ]; then -		exec > /dev/tty -		printf "\n$(gettext "Cannot write to %s, exiting").\n" $KRB5_CONFIG_FILE +	exec 3>$KRB5_CONFIG +	if [[ $? -ne 0 ]]; then +		printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG  		error_message  	fi -	printf "[libdefaults]\n" -	if [ "$dns_lookup" = yes ]; then -	    printf "\t$dnsarg = on\n" -	    if [ "$dnsarg" = dns_lookup_kdc ]; then -		printf "\tdefault_realm = $REALM\n" -		printf "\n[domain_realm]\n" -		printf "\t$KDC = $REALM\n" -		printf "\t$client_machine = $REALM\n" -		printf "\t.$fqdn = $REALM\n\n" +	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 +		if [[ -n $fkdc_list ]]; then +			for kdc in $fkdc_list; do +				printf "\t$kdc = $realm\n" 1>&3 +			done +		fi +		printf "\t$FKDC = $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 "\n[realms]\n" -		    printf "\t$REALM = {\n" -		    printf "\t\tkdc = $KDC\n" -		    printf "\t\tadmin_server = $KDC\n" -		    printf "\t}\n\n" +		if [[ $dnsarg = dns_lookup_realm ]]; then + +		    printf "\n[realms]\n" 1>&3 +		    printf "\t$realm = {\n" 1>&3 +		    if [[ -n $kdc_list ]]; then +			for kdc in $kdc_list; do +				printf "\t\tkdc = $kdc\n" 1>&3 +			done +		    else +		    	printf "\t\tkdc = $KDC\n" 1>&3 +		    fi +		    printf "\t\tadmin_server = $KDC\n" 1>&3 +		    if [[ $non_solaris == yes ]]; then +			printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3 +		    fi +		    printf "\t}\n\n" 1>&3  		else -		    printf "\n\n" +		    printf "\n\n" 1>&3  		fi  	    fi  	else -	    printf "\tdefault_realm = $REALM\n\n" +	    printf "\tdefault_realm = $realm\n\n" 1>&3 -	    printf "[realms]\n" -	    printf "\t$REALM = {\n" -	    printf "\t\tkdc = $KDC\n" -	    printf "\t\tadmin_server = $KDC\n" -	    printf "\t}\n\n" +	    printf "[realms]\n" 1>&3 +	    printf "\t$realm = {\n" 1>&3 +	    if [[ -n $kdc_list ]]; then +		for kdc in $kdc_list; do +			printf "\t\tkdc = $kdc\n" 1>&3 +		done +	    else +	    	printf "\t\tkdc = $KDC\n" 1>&3 +	    fi +	    printf "\t\tadmin_server = $KDC\n" 1>&3 +	    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" -	    printf "\t$KDC = $REALM\n" -	    printf "\t$client_machine = $REALM\n" -	    printf "\t.$fqdn = $REALM\n\n" +	    printf "[domain_realm]\n" 1>&3 +	    if [[ -n $fkdc_list ]]; then +		for kdc in $fkdc_list; do +			printf "\t$kdc = $realm\n" 1>&3 +		done +	    fi +	    printf "\t$FKDC = $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" -	printf "\tdefault = FILE:/var/krb5/kdc.log\n" -	printf "\tkdc = FILE:/var/krb5/kdc.log\n" +	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 -	# -	# return output to TTY -	# -	exec > /dev/tty +	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  } -ask() { -	question=$1 -	default_answer=$2 -	if [ -z "$default_answer" ]; then -		printf "$question :\c" +function ask { +	typeset question=$1 +	typeset default_answer=$2 + +	if [[ -z $default_answer ]]; then +		printf "$question :"  	else -		printf "$question [$default_answer]: \c" +		printf "$question [$default_answer]: "  	fi  	read answer  	test -z "$answer" && answer="$default_answer"  } -yesno() { +function yesno {  	typeset question="$1" -	answer="" -	while [ -z "$answer" ]; do -		ask "$question" y/n -		case "$answer" in -			y|yes)	answer=yes;; -			n|no)	answer=no;; -			*)	answer="";; + +	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  } -query() { +function query {  	yesno "$*" -	if [ "$answer" = no ]; then + +	if [[ $answer == no ]]; then  		printf "\t$(gettext "No action performed").\n"  	fi  } -read_profile() { +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 +			case $param in +			REALM)  if [[ -z $realm ]]; then +					realm="$value" +					checkval="REALM"; check_value $realm  				fi  				;; -			KDC)    if [ -z "$KDC" ]; then +			KDC)    if [[ -z $KDC ]]; then  					KDC="$value"  					checkval="KDC"; check_value $KDC  				fi  				;; -			ADMIN)  if [ -z "$ADMIN_PRINC" ]; then +			ADMIN)  if [[ -z $ADMIN_PRINC ]]; then  					ADMIN_PRINC="$value"  					checkval="ADMIN_PRINC"      					check_value $ADMIN_PRINC  				fi  				;; -			FILEPATH)  if [ -z "$filepath" ]; then +			FILEPATH)  if [[ -z $filepath ]]; then  					filepath="$value"  				   fi  				   ;; -			NFS)    if [ -z "$add_nfs" ]; then -				    if [ "$value" = 1 ]; then +			NFS)    if [[ -z $add_nfs ]]; then +				    if [[ $value == 1 ]]; then  					    add_nfs=yes  				    else  					    add_nfs=no  				    fi  				fi  				;; -			DNSLOOKUP) if [ -z "$dnsarg" ]; then +			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 +			FQDN) if [[ -z $fqdnlist ]]; then  					fqdnlist="$value"  					checkval="FQDN"  					check_value $fqdnlist  					verify_fqdnlist "$fqdnlist"  			      fi  			      ;; +			MSAD) if [[ -z $msad ]]; then +				if [[ $value == 1 ]]; then +					msad=yes +					non_solaris=yes +				else +					msad=no +				fi +			      fi +			      ;;  			esac  		done <$file  	else -		printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file +		printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file >&2  		error_message  	fi  } -ping_check() { +function ping_check {  	typeset machine="$1"  	typeset string="$2" -	if ping $machine > /dev/null; then + +	if ping $machine 2 > /dev/null 2>&1; then  		:  	else -		printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine +		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 +	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.  Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n" +		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\n" >&2 +break  	fi  } -check_value() { +function check_value {  	typeset arg="$1" -	if [ -z "$arg" ]; then -		printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval + +	if [[ -z $arg ]]; then +		printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval >&2  		error_message  	else -		echo "$arg">$TMP_FILE +		echo "$arg" > $TMP_FILE  		if egrep -s '[*$^#!]+' $TMP_FILE; then -			printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval +			printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval >&2  			error_message  		fi  	fi  } -set_dns_value() { -	typeset arg="$1" -	if [[ "$arg" = dns_lookup_kdc  ||  "$arg" = dns_lookup_realm  || "$arg" = dns_fallback ]]; then +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 -		arg=$(echo "$arg"|tr '[A-Z]' '[a-z]') -		if [ "$arg" = none ]; then +		if [[ $arg == none ]]; then  			dns_lookup=no  		else -			printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" +			printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" >&2  			error_message  		fi  	fi  } -verify_fqdnlist() { -	integer count=1 +function verify_kdcs { +	typeset k_list="$1" +	typeset -l kdc +	typeset list fqhn f_list + +	kdc_list=$(echo "$k_list" | sed 's/,/ /g') + +	if [[ -z $k_list ]]; then +		printf "\n$(gettext "At least one KDC should be listed").\n\n" >&2 +		usage +	fi + +	for kdc in $k_list; do +		if [[ $kdc != $KDC ]]; then +			list="$list $kdc" +			fkdc=`$KLOOKUP $kdc` +			if ping $fkdc 2 > /dev/null; then +				: +			else +				printf "\n$(gettext "%s %s is unreachable, no action performed").\n" "KDC" $fkdc >&2 +			fi +			f_list="$f_list $fkdc" +		fi +	done + +	fkdc_list="$f_list" +	kdc_list="$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 fqdnlist eachfqdn tmpvar fullhost -	list=$(echo "$1" | tr -d " " | tr -d "\t") -	hostname=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1) +	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, atleast one FQDN should be listed").\n\n" - +	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 +		while [[ ! -z $eachfqdn ]]; do  			tmpvar=$(echo "$eachfqdn" | cut -d"." -f1) -			if [ -z "$tmpvar" ]; then +			if [[ -z $tmpvar ]]; then  				fullhost="$hostname$eachfqdn"  			else  				fullhost="$hostname.$eachfqdn"  			fi -			ping_check $fullhost "System" -			if [ "$fullhost" = "$client_machine" ]; then +			ping_check $fullhost $(gettext "System") +			if [[ $fullhost == $client_machine ]]; then  				:  			else  				fqdnlist="$fqdnlist $fullhost"  			fi -			if [[ "$list" == *,* ]]; then +			if [[ $list == *,* ]]; then  				((count = count + 1))  				eachfqdn=$(echo "$list" | cut -d"," -f$count)  			else @@ -341,51 +564,1016 @@ verify_fqdnlist() {  	fi  } -usage() { -	printf "\n$(gettext "Usage: kclient [ -n ] [ -R realm ] [ -k kdc ] [ -a adminuser ] [ -c filepath ] [ -d dnsarg ] [ -f fqdn_list ] [ -p profile ]")\n\n" -	printf "$(gettext "Refer kclient(1M) for details, exiting").\n" +function setup_keytab { +	typeset cname ask_fqdns current_release + +	# +	# 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 + +	[[ -n $msad ]] && 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 +		echo +		query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?" +		ask_fqdns=$answer +		if [[ $ask_fqdns == yes ]]; then +			printf "$(gettext "Enter a comma-seperated 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 logical_hn + +	echo "$logical_hn" > $TMP_FILE +	if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then +		# do nothing, logical_hn 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 +			logical_hn=$(echo "$logical_hn.$fqdn") +		fi +	fi + +	client_machine=$logical_hn + +	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 mul +tiple mappings of doamin 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 ]  master KDC server host name")\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  	error_message  } +function discover_domain { +	typeset dom DOMs + +	if [[ -z $realm ]]; then +		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs S` +	else +		set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs.$realm S` +	fi + +	[[ -z ${DOMs[0]} ]] && return 1 + +	dom=${DOMs[0]} + +	dom=${dom#*.} +	dom=${dom% *} + +	domain=$dom + +	return 0 +} + +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 canon_resolve { +	typeset name ip + +	name=`$KLOOKUP $1 C` +	[[ -z $name ]] && name=`$KLOOKUP $1 A` +	[[ -z $name ]] && return + +	ip=`$KLOOKUP $name I` +	[[ -z $ip ]] && return + +	cname=`$KLOOKUP $ip P` +	[[ -z $cname ]] && return + +	print -- "$cname" +} + +function rev_resolve { +	typeset name ip + +	ip=`$KLOOKUP $1 I` + +	[[ -z $ip ]] && return +	name=`$KLOOKUP $ip P` +	[[ -z $name ]] && return + +	print -- $name +} + +# 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 +		print -- $srv $port +	done +} + +function getKDC { +	typeset j + +	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 + +	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 { +	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 +		error_message +	fi + +	printf "[libdefaults]\n" 1>&3 +	printf "\tdefault_realm = $realm\n" 1>&3 +	printf "\n[realms]\n" 1>&3 +	printf "\t$realm = {\n" 1>&3 +	for i in ${KDCs[@]} +	do +		[[ $i == +([0-9]) ]] && continue +		printf "\t\tkdc = $i\n" 1>&3 +	done +	# Defining the same as admin_server.  This would cause auth failures +	# if this was different. +	printf "\n\t\tkpasswd_server = $KDC\n" 1>&3 +	printf "\n\t\tadmin_server = $KDC\n" 1>&3 +	printf "\t\tkpasswd_protocol = SET_CHANGE\n\t}\n" 1>&3 +	printf "\n[domain_realm]\n" 1>&3 +	printf "\t.$dom = $realm\n\n" 1>&3 +	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\t}\n" 1>&3 +} + +function getForestName { +	ldapsearch -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" +		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 +} + +function ipAddr2num { +	typeset OIFS +	typeset -i16 num byte + +	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 +} + +function num2ipAddr { +	typeset -i16 num +	typeset -i10 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 +} + +function netmask2length { +	typeset -i16 netmask +	typeset -i len + +	netmask=$1 +	len=32 +	while [[ $((netmask % 2)) -eq 0 ]] +	do +		netmask=$((netmask>>1)) +		len=$((len - 1)) +	done +	print $len +} + +function getSubnets { +	typeset -i16 addr netmask +	typeset -i16 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 -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) +		ldapsearch -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 + +	cp $KRB5_CONFIG $KRB5_CONFIG_FILE +	chmod 0644 $KRB5_CONFIG_FILE +	cp $new_keytab $KRB5_KEYTAB_FILE +	chmod 0600 $KRB5_KEYTAB_FILE +} + +function addDNSRR { +	smbFMRI=svc:/network/smb/server:default +	ddnsProp=smbd/ddns_enable +	enProp=general/enabled + +	enabled=`svcprop -p $enProp $smbFMRI` +	ddns_enable=`svcprop -p $ddnsProp $smbFMRI` + +	if [[ $enabled == true && $ddns_enable != true ]]; then +		printf "$(gettext "Warning: won't create DNS records for client").\n" +		printf "$(gettext "%s property not set to 'true' for the %s FMRI").\n" $ddnsProp $smbFMRI +		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 $1 > /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: wasn't able 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 +	smbFMRI=svc:/network/smb/server + +	printf "%s" $newpw | $KSMB -d $domain -s $server +	if [[ $? -ne 0 ]]; then +		printf "$(gettext "Warning: wasn't able to set %s domain, server, and password information").\n" $smbFMRI +		return +	fi + +	svcadm refresh $smbFMRI +	if [[ $? -ne 0 ]]; then +		printf "$(gettext "Warning: wasn't able to set refresh %s domain, server, and password information").\n" $smbFMRI +	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 | 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" +			error_message +		fi +	fi +} + +function getKDCDC { + +	getKDC +	if [[ -n $kdc ]]; then +		KDC=$kdc +		dc=$kdc +	else +		getDC +		if [[ -n $dc ]]; then +			KDC=$dc +		else +			printf "$(gettext "Could not find domain controller server for '%s'.  Exiting").\n" $realm +			error_message +		fi +	fi +} + +function join_domain { +	typeset -u upcase_nodename +	typeset netbios_nodename fqdn +	 +	container=Computers +	ldap_args="-o authzid= -o mech=gssapi" +	userAccountControlBASE=4096 + +	if [[ -z $ADMIN_PRINC ]]; then +		cprinc=Administrator +	else +		cprinc=$ADMIN_PRINC +	fi + +	if ! discover_domain; then +		printf "$(gettext "Can not find realm") '%s'.\n" $realm +		error_message +	fi + +	dom=$domain +	realm=$domain +	upcase_nodename=$hostname +	netbios_nodename="${upcase_nodename}\$" +	fqdn=$hostname.$domain +	upn=host/${fqdn} + +	grep=/usr/xpg4/bin/grep + +	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 + +	grep=/usr/xpg4/bin/grep + +	modify_existing=false +	recreate=false + +	DomainDnsZones=$(rev_resolve DomainDnsZones.$dom.) +	ForestDnsZones=$(rev_resolve ForestDnsZones.$dom.) + +	getBaseDN "$container" "$dom" + +	if [[ -n $KDC ]]; then +		dc=$KDC +	else +		getKDCDC +	fi + +	write_ads_krb5conf + +	printf "$(gettext "Attempting to join the '%s' domain").\n\n" $realm + +	kinit $cprinc@$realm +	if [[ $? -ne 0 ]]; then +		printf "$(gettext "Could not authenticate %s.  Exiting").\n" $cprinc@$realm +		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 +		getGC + +		write_ads_krb5conf +	fi + +	if [[ ${#GCs} -eq 0 ]]; then +		printf "$(gettext "Could not find global catalogs.  Exiting").\n" +		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 -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" +		error_message +	fi +	# Longhorn and above can't perform an init auth from service +	# keys if the realm is included in the UPN.  w2k3 and below +	# can't perform an init auth when the realm is excluded. +	[[ $level -lt 3 ]] && upn=${upn}@${realm} + +	if ldapsearch -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" +		error_message +	fi +	ldapsearch -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. +	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 -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} +				error_message +			fi +		elif $modify_existing; then +			: # Nothing to delete +		else +			printf "$(gettext "A machine account already exists").\n" +			error_message +		fi +	fi + +	if $modify_existing; then +		cat > "$object" <<EOF +dn: CN=$upcase_nodename,$baseDN +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" +		ldapadd -h "$dc" $ldap_args -f "$object"  +		if [[ $? -ne 0 ]]; then +			printf "$(gettext "Failed to create the AD object via LDAP").\n" +			error_message +		fi +	else +		cat > "$object" <<EOF +dn: CN=$upcase_nodename,$baseDN +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" + +		ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1 +		if [[ $? -ne 0 ]]; then +			printf "$(gettext "Failed to create the AD object via LDAP").\n" +			error_message +		fi +	fi + +	# Generate a new password for the new account +	MAX_PASS=32 +        i=0 + +	while : +	do +		while ((MAX_PASS > i)) +		do +			# 94 elements in the printable character set starting +			# at decimal 33, contiguous. +			dig=$((RANDOM%94+33)) +			c=$(printf "\\`printf %o $dig`\n") +			p=$p$c +			((i+=1)) +		done + +		# Ensure that we have four character classes. +		d=${p%[[:digit:]]*} +		a=${p%[[:lower:]]*} +		A=${p%[[:upper:]]*} +		x=${p%[[:punct:]]*} + +		# Just compare the number of characters from what was previously +		# matched.  If there is a difference then we found a match. +		n=${#p} +		[[ ${#d} -ne $n && ${#a} -ne $n && \ +		   ${#A} -ne $n && ${#x} -ne $n ]] && break +		i=0 +		p= +	done +	newpw=$p + +	# 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" +		error_message +	fi + +	# Lookup the new principal's kvno: +	ldapsearch -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+1+2)) +		enctypes[${#enctypes[@]}]=des-cbc-crc +		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" +		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: CN=$upcase_nodename,$baseDN +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: CN=$upcase_nodename,$baseDN +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" +		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: CN=$upcase_nodename,$baseDN +changetype: modify +add: servicePrincipalName +servicePrincipalName: nfs/${fqdn} +servicePrincipalName: HTTP/${fqdn} +servicePrincipalName: root/${fqdn} +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" +		error_message +	fi + +	printf "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" host/${fqdn}@${realm} > /dev/null 2>&1 +	if [[ $? -ne 0 ]] +	then +		printf "$(gettext "Failed to set account password").\n" +		error_message +	fi + +	# Could be setting ${netbios_nodename}@${realm}, but for now no one +	# is requesting this. + +	print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" nfs/${fqdn}@${realm} > /dev/null 2>&1 +	if [[ $? -ne 0 ]] +	then +		printf "$(gettext "Failed to set account password").\n" +		error_message +	fi + +	print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" HTTP/${fqdn}@${realm} > /dev/null 2>&1 +	if [[ $? -ne 0 ]] +	then +		printf "$(gettext "Failed to set account password").\n" +		error_message +	fi + +	print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" root/${fqdn}@${realm} > /dev/null 2>&1 +	if [[ $? -ne 0 ]] +	then +		printf "$(gettext "Failed to set account password").\n" +		error_message +	fi + +	doKRB5config + +	addDNSRR $dom + +	setSMB $dom $dc + +	printf -- "\n---------------------------------------------------\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  # -KRB5_CONFIG_FILE="/etc/krb5/krb5.conf" -KRB5_KEYTAB_FILE="/etc/krb5/krb5.keytab" -RESOLV_CONF_FILE="/etc/resolv.conf" -NFSSEC_FILE="/etc/nfssec.conf" -dns_lookup="no" -ask_fqdns="no" +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;	check_bin $KSMB +KDYNDNS=/usr/lib/krb5/kdyndns;	check_bin $KDYNDNS + +dns_lookup=no +ask_fqdns=no +adddns=no  checkval=""  profile="" +typeset -u realm +typeset -l hostname KDC -# Set OS release level to Solaris 10, inorder to track the requirement -# of the root/fqdn service principal for kerberized NFS. -release_level=10 +export TMPDIR="/var/run/kclient" -if [ -x /usr/bin/mktemp ]; then -	TMP_FILE=$(/usr/bin/mktemp /etc/krb5/krb5tmpfile.XXXXXX) -	TMP_CCACHE=$(/usr/bin/mktemp /etc/krb5/krb5tmpccache.XXXXXX) -else -	TMP_FILE="/etc/krb5/krb5tmpfile.$$" -	TMP_CCACHE="/etc/krb5/krb5tmpccache.$$" -fi +mkdir $TMPDIR > /dev/null 2>&1 -if [[ -z "$TMP_FILE" || -z "$TMP_CCACHE" ]]; then -	printf "\n$(gettext "Temporary file creation failed, exiting").\n" >&2 -	exit 1 +TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX) +export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX) +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 file, exiting").\n" >&2 +	error_message  fi  #  # If we are interrupted, cleanup after ourselves  # -trap "/usr/bin/rm -f $TMP_FILE $TMP_CCACHE; exit 1" HUP INT QUIT TERM +trap "exiting 1" HUP INT QUIT TERM -if [ -d /usr/bin ]; then -	if [ -d /usr/sbin ]; then +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 @@ -398,77 +1586,55 @@ else  fi  printf "\n$(gettext "Starting client setup")\n\n" -printf "---------------------------------------------------\n" +printf -- "---------------------------------------------------\n"  #  # Check for uid 0, disallow otherwise  #  id 1>$TMP_FILE 2>&1 -if [ $? -eq 0 ]; then +if [[ $? -eq 0 ]]; then  	if egrep -s "uid=0\(root\)" $TMP_FILE; then  		# uid is 0, go ahead ...  		:  	else -		printf "\n$(gettext "Root privileges are required to run this script, exiting").\n" +		printf "\n$(gettext "Administrative privileges are required to run this script, exiting").\n" >&2  		error_message  	fi  else  	cat $TMP_FILE; -	printf "\n$(gettext "uid check failed, exiting").\n" -	error_message -fi - -# -# Check for /etc/resolv.conf -# -if [ -r $RESOLV_CONF_FILE ]; then -	while read label text -	do -		case "$label" in -		domain) # Copy the entry into $fqdn -			if [ -z "$text" ]; then -				printf "\n$(gettext "DNS domain info malformed in %s, exiting").\n" $RESOLV_CONF_FILE -				error_message -			fi -			fqdn=$(echo "$text"|tr '[A-Z]' '[a-z]') -			break -			;; -		esac -	done <$RESOLV_CONF_FILE - -	if [ -z "$fqdn" ]; then -		printf "\n$(gettext "DNS domain info missing in %s, exiting").\n" $RESOLV_CONF_FILE -		error_message -	fi -else -	# -	# /etc/resolv.conf not present, exit ... -	# -	printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE -	printf "$(gettext "Refer resolv.conf(4), exiting").\n" +	printf "\n$(gettext "uid check failed, exiting").\n" >&2  	error_message  fi -client_machine=$(uname -n | tr '[A-Z]' '[a-z]' | cut -d"." -f1).$fqdn +uname=$(uname -n) +hostname=${uname%%.*}  #  # Process the command-line arguments (if any)  #  OPTIND=1 -while getopts np:R:k:a:c:d:f: OPTIONS +while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T: OPTIONS  do  	case $OPTIONS in -	    p) options="$options -p" -	       profile="$OPTARG" -	       read_profile $profile +	    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 +	       realm="$OPTARG" +	       checkval="REALM"; check_value $realm  	       ;; -	    k) options="$options -k" -	       KDC="$OPTARG" -	       checkval="KDC"; check_value $KDC +	    T) options="$options -T" +	       type="$OPTARG" +	       if [[ $type == ms_ad ]]; then +		msad=yes +		adddns=yes +	       else +		non_solaris=yes +		no_keytab=yes +	       fi  	       ;;  	    a) options="$options -a"  	       ADMIN_PRINC="$OPTARG" @@ -483,13 +1649,31 @@ do  	       ;;  	    f) options="$options -f"  	       fqdnlist="$OPTARG" -	       verify_fqdnlist "$fqdnlist"   	       ;; +	    h) options="$options -h" +	       logical_hn="$OPTARG" +	       checkval="LOGICAL_HOSTNAME"; check_value $logical_hn +	       ;; +	    k) options="$options -k" +	       kdc_list="$OPTARG" +	       ;; +	    m) options="$options -m" +	       KDC="$OPTARG" +	       checkval="KDC"; check_value $KDC +	       ;;  	    n) options="$options -n"  	       add_nfs=yes  	       ;; +	    p) options="$options -p" +	       profile="$OPTARG" +	       read_profile $profile +	       ;; +	    s) options="$options -s" +	       svc_list="$OPTARG" +	       SVCs=${svc_list//,/ } + 	       ;;  	    \?) usage -		;; +	       ;;  	    *) usage  	       ;;  	esac @@ -498,19 +1682,75 @@ done  #correct argument count after options  shift `expr $OPTIND - 1` -if [ -z "$options" ]; then +if [[ -z $options ]]; then  	:  else -	if [ $# -ne 0 ]; then +	if [[ $# -ne 0 ]]; then  		usage  	fi  fi -if [ -z "$dnsarg" ]; then +# +# 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 DN +S for hosts and/or ipnodes").\n" + +# +# 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 (MIT, Heimdal, Shishi, etc.)") ?" +	non_solaris=$answer +	if [[ $non_solaris == yes ]]; then +		no_keytab=yes +	else +		query "$(gettext "Is this a client of a Microsoft Active Directory (MS AD) server") ?" +		if [[ $answer == yes ]]; then +			msad=yes +		fi +	fi +fi + +[[ $msad == yes ]] && join_domain + +[[ -n $fqdnlist ]] && verify_fqdnlist "$fqdnlist" + +if [[ -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"): \c" +	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 @@ -519,166 +1759,163 @@ else  	set_dns_value $dnsarg  fi -if [ -z "$REALM" ]; then -	printf "$(gettext "Enter the Kerberos realm"): \c" -	read REALM -	checkval="REALM"; check_value $REALM +if [[ -n $kdc_list ]]; then +	if [[ -z $KDC ]]; then +		for kdc in $kdc_list; do +			break +		done +		KDC="$kdc" +	fi +fi + +if [[ -z $realm && -z $filepath ]]; then +	printf "$(gettext "Enter the Kerberos realm"): " +	read realm +	checkval="REALM"; check_value $realm  fi -if [ -z "$KDC" ]; then -	printf "$(gettext "Specify the KDC hostname for the above realm"): \c" +if [[ -z $KDC && -z $filepath ]]; then +	printf "$(gettext "Specify the master KDC hostname for the above realm"): "  	read KDC  	checkval="KDC"; check_value $KDC  fi -REALM=$(echo "$REALM"|tr '[a-z]' '[A-Z]') -KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]') +FKDC=`$KLOOKUP $KDC` -echo "$KDC">$TMP_FILE -if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then -	# do nothing, KDC is in fqdn format -	: -	echo "$KDC" -else -	if egrep -s '\.+' $TMP_FILE; then -		printf "\n$(gettext "Improper format of KDC hostname, exiting").\n" -		error_message -	else -		# Attach fqdn to KDC, to get the Fully Qualified Domain Name -		# of the KDC requested -		KDC=$(echo "$KDC.$fqdn") -	fi -fi  #  # Ping to see if the kdc is alive !  # -ping_check $KDC "KDC" - +ping_check $FKDC "KDC"  # -# Start writing up the krb5.conf file, save the existing one -# if already present +# Check to see if we will have a dynamic presence in the realm  # -writeup_krb5_conf - +if [[ -z $options ]]; then +	query "$(gettext "Will this client need service keys") ?" +	if [[ $answer == no ]]; then +		no_keytab=yes +	fi +fi  # -# Done creating krb5.conf, so now we ... +# Check to see if we are configuring the client to use a logical host name +# of a cluster environment  # -# 1. kinit with ADMIN_PRINC -# - -if [ -z "$ADMIN_PRINC" ]; then -	printf "\n$(gettext "Enter the krb5 administrative principal to be used"): \c" -	read ADMIN_PRINC -	checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC +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 +		setup_lhn +	fi  fi -echo "$ADMIN_PRINC">$TMP_FILE - -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" -		error_message -	else -		ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin") +if [[ -z $options || -z $filepath ]]; then +	query "$(gettext "Do you have any slave KDC(s)") ?" +	if [[ $answer == yes ]]; then +		printf "$(gettext "Enter a comma-seperated list of slave KDC host names"): " +		read kdc_list  	fi  fi -printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC -KDC=$(echo "$KDC"|tr '[A-Z]' '[a-z]') -kinit -c $TMP_CCACHE -S kadmin/$KDC $ADMIN_PRINC -klist -c $TMP_CCACHE 1>$TMP_FILE 2>&1 -if egrep -s "Valid starting" $TMP_FILE && egrep -s "kadmin/$KDC@$REALM" $TMP_FILE; then -    	: -else -	printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC -	error_message +[[ -n $kdc_list ]] && verify_kdcs "$kdc_list" + +if [[ -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-seperated list of domain/hosts +to map to the default realm"): " +		read domain_list +	fi  fi +[[ -n domain_list ]] && domain_list=${domain_list//,/ }  # -# 2. Do we want to create and/or add service principal(s) for fqdn's -#    other than the one listed in resolv.conf(4) ? +# Start writing up the krb5.conf file, save the existing one +# if already present  # -if [ -z "$options" ]; then -	echo -	query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $REALM ?" -	ask_fqdns=$answer -	if [ "$ask_fqdns" = yes ]; then -		printf "$(gettext "Enter a comma-seperated list of DNS domain names"): \c" -		read fqdnlist -		verify_fqdnlist "$fqdnlist" -	else -		fqdnlist=$client_machine -	fi -else -	if [ -z "$fqdnlist" ]; then -		fqdnlist=$client_machine -	fi -fi +writeup_krb5_conf  # -# 3. Set up keytab/config files for nfs/host/root entries (if requested) +# Is this client going to use krb-nfs?  If so then we need to at least +# uncomment the krb5* sec flavors in nfssec.conf.  #  echo -if [ -z "$options" ]; then +if [[ -z $options ]]; then  	query "$(gettext "Do you plan on doing Kerberized nfs") ?"  	add_nfs=$answer  fi -if [ "$add_nfs" = yes ]; then +if [[ $add_nfs == yes ]]; then  	modify_nfssec_conf -	echo; call_kadmin nfs -	# -	# Check to see if the system is a pre-S10 system which would -	# require the root/FQDN svc principal for kerberized NFS. + +	#	 +	# We also want to enable gss as we now live in a SBD world  	# -	current_release=$(uname -r | cut -d"." -f2) -	if [ $current_release -lt $release_level ]; then -		echo; call_kadmin root -	fi +	svcadm enable svc:/network/rpc/gss:default +	[[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable gss service").\n"  fi -# Add the host entry to the keytab -echo; call_kadmin host +if [[ -z $options ]]; then +	query "$(gettext "Do you want to update /etc/pam.conf") ?" +	if [[ $answer == yes ]]; then +		printf "$(gettext "Enter a list of PAM service names in the following format: service:{first|only|optional}[,..]"): " +		read svc_list +		SVCs=${svc_list//,/ } +	fi +fi +[[ -n $svc_list ]] && update_pam_conf  # -# 4. Copy over krb5.conf master copy from filepath +# Copy over krb5.conf master copy from filepath  # -if [ -z "$options" ]; then +if [[ -z $options || -z $filepath ]]; then  	echo  	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"): \c" +	if [[ $answer == yes ]]; then +		printf "$(gettext "Enter the pathname of the file to be copied"): "  		read filepath  	fi  fi -if [ -z "$filepath" ]; then -	: +if [[ -z $filepath ]]; then +	doKRB5config  else -	if [ -r $filepath ]; then +	if [[ -r $filepath ]]; then  		cp $filepath $KRB5_CONFIG_FILE -		if [ $? -eq 0 ]; then +		if [[ $? -eq 0 ]]; then  			printf "\n$(gettext "Copied %s").\n" $filepath  		else -			printf "\n$(gettext "Copy of %s failed, exiting").\n" $filepath +			printf "\n$(gettext "Copy of %s failed, exiting").\n" $filepath >&2  			error_message  		fi  	else -		printf "\n$(gettext "%s not found, exiting").\n" $filepath +		printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2  		error_message  	fi  fi -printf "\n---------------------------------------------------\n" -printf "$(gettext "Setup COMPLETE").\n" +# +# Populate any service keys needed for the client in the keytab file +# +[[ $no_keytab != yes ]] && setup_keytab + +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  # -# 5. Cleanup, please ! +# Cleanup.  # -kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1 +kdestroy -q 1>$TMP_FILE 2>&1  rm -f $TMP_FILE +rm -rf $TMPDIR > /dev/null 2>&1  exit 0 diff --git a/usr/src/cmd/krb5/kadmin/kclient/kdyndns.c b/usr/src/cmd/krb5/kadmin/kclient/kdyndns.c new file mode 100644 index 0000000000..ebd5e43212 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/kdyndns.c @@ -0,0 +1,85 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident	"%Z%%M%	%I%	%E% SMI" + +#include <stdio.h> +#include <locale.h> +#include <netdb.h> +#include <smbsrv/libsmbns.h> + +char *whoami = NULL; + +static void usage(); + +static +void +usage() +{ +	fprintf(stderr, gettext("Usage: %s -d fqdn\n"), whoami); +	fprintf(stderr, +	    gettext("\t-d\tThe fully qualified domain of the client\n")); +	exit(1); +} + +int +main(int argc, char **argv) +{ +	char c, fqdn[MAXHOSTNAMELEN]; +	int ret = 0; + +	(void) setlocale(LC_ALL, ""); + +#if !defined(TEXT_DOMAIN) +#define	TEXT_DOMAIN "SYS_TEST" +#endif /* TEXT_DOMAIN */ + +	(void) textdomain(TEXT_DOMAIN); + +	whoami = argv[0]; + +	while ((c = getopt(argc, argv, "d:")) != -1) { +		switch (c) { +		case 'd': +			(void) strncpy(fqdn, optarg, sizeof (fqdn)); +			break; +		default: +			usage(); +			break; +		} +	} + +	if (argc != optind) +		usage(); + +	/* +	 * Update DNS RR for the client using DynDNS.  First it tries the +	 * unauthed version then it tries the GSS version. +	 */ +	ret = dyndns_update(fqdn); + +	return (ret); +} diff --git a/usr/src/cmd/krb5/kadmin/kclient/ksetpw.c b/usr/src/cmd/krb5/kadmin/kclient/ksetpw.c new file mode 100644 index 0000000000..07fff409b1 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/ksetpw.c @@ -0,0 +1,384 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident	"%Z%%M%	%I%	%E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <locale.h> +#include <netdb.h> +#include "k5-int.h" + +#define	QUOTE(x)	#x +#define	VAL2STR(x)	QUOTE(x) + +static char *whoami = NULL; + +static void kt_add_entry(krb5_context ctx, krb5_keytab kt, +	const krb5_principal princ, krb5_enctype enctype, krb5_kvno kvno, +	const char *pw); + +static krb5_error_code kt_remove_entries(krb5_context ctx, krb5_keytab kt, +	const krb5_principal princ); + +static void usage(); + +int +main(int argc, char **argv) +{ +	krb5_context ctx = NULL; +	krb5_error_code code = 0; +	krb5_enctype *enctypes; +	int enctype_count = 0; +	krb5_ccache cc = NULL; +	krb5_keytab kt = NULL; +	krb5_kvno kvno = 1; +	krb5_principal victim; +	char c, *vprincstr, *ktname, *token, *lasts, *newpw; +	int result_code, i, len, nflag = 0; +	krb5_data result_code_string, result_string; + +	(void) setlocale(LC_ALL, ""); + +#if !defined(TEXT_DOMAIN) +#define	TEXT_DOMAIN "SYS_TEST" +#endif /* TEXT_DOMAIN */ + +	(void) textdomain(TEXT_DOMAIN); + +	/* Misc init stuff */ +	(void) memset(&result_code_string, 0, sizeof (result_code_string)); +	(void) memset(&result_string, 0, sizeof (result_string)); + +	whoami = argv[0]; + +	code = krb5_init_context(&ctx); +	if (code != 0) { +		com_err(whoami, code, gettext("krb5_init_context() failed")); +		exit(1); +	} + +	while ((c = getopt(argc, argv, "v:c:k:e:n")) != -1) { +		switch (c) { +		case 'n': +			nflag++; +			break; +		case 'k': +			if (kt != NULL) +				usage(); +			len = snprintf(NULL, 0, "WRFILE:%s", optarg) + 1; +			if ((ktname = malloc(len)) == NULL) { +				(void) fprintf(stderr, +				    gettext("Couldn't allocate memory\n")); +				exit(1); +			} +			(void) snprintf(ktname, len, "WRFILE:%s", optarg); +			if ((code = krb5_kt_resolve(ctx, ktname, &kt)) != 0) { +				com_err(whoami, code, +				    gettext("Couldn't open/create " +				    "keytab %s"), optarg); +				exit(1); +			} +			break; +		case 'c': +			if (cc != NULL) +				usage(); +			if ((code = krb5_cc_resolve(ctx, optarg, &cc)) != 0) { +				com_err(whoami, code, +				    gettext("Couldn't open ccache %s"), optarg); +				exit(1); +			} +			break; +		case 'e': +			len = strlen(optarg); +			token = strtok_r(optarg, ",\t,", &lasts); + +			if (token == NULL) +				usage(); + +			do { +				if (enctype_count++ == 0) { +					enctypes = malloc(sizeof (*enctypes)); +				} else { +					enctypes = realloc(enctypes, +					    sizeof (*enctypes) * enctype_count); +				} +				if (enctypes == NULL) { +					(void) fprintf(stderr, gettext +					    ("Couldn't allocate memory")); +					exit(1); +				} +				code = krb5_string_to_enctype(token, +				    &enctypes[enctype_count - 1]); + +				if (code != 0) { +					com_err(whoami, code, gettext("Unknown " +					    "or unsupported enctype %s"), +					    optarg); +					exit(1); +				} +			} while ((token = strtok_r(NULL, ",\t ", &lasts)) != +			    NULL); +			break; +		case 'v': +			kvno = (krb5_kvno) atoi(optarg); +			break; +		default: +			usage(); +			break; +		} +	} + +	if (nflag && enctype_count == 0) +		usage(); + +	if (nflag == 0 && cc == NULL && +	    (code = krb5_cc_default(ctx, &cc)) != 0) { +		com_err(whoami, code, gettext("Could not find a ccache")); +		exit(1); +	} + +	if (enctype_count > 0 && kt == NULL && +	    (code = krb5_kt_default(ctx, &kt)) != 0) { +		com_err(whoami, code, gettext("No keytab specified")); +		exit(1); +	} + +	if (argc != (optind + 1)) +		usage(); + +	vprincstr = argv[optind]; +	code = krb5_parse_name(ctx, vprincstr, &victim); +	if (code != 0) { +		com_err(whoami, code, gettext("krb5_parse_name(%s) failed"), +		    vprincstr); +		exit(1); +	} + +	if (!isatty(fileno(stdin))) { +		char buf[PASS_MAX + 1]; + +		if (scanf("%" VAL2STR(PASS_MAX) "s", &buf) != 1) { +			(void) fprintf(stderr, +			    gettext("Couldn't read new password\n")); +			exit(1); +		} + +		newpw = strdup(buf); +		if (newpw == NULL) { +			(void) fprintf(stderr, +			    gettext("Couldn't allocate memory\n")); +			exit(1); +		} +	} else { +		newpw = getpassphrase(gettext("Enter new password: ")); +		if (newpw == NULL) { +			(void) fprintf(stderr, +			    gettext("Couldn't read new password\n")); +			exit(1); +		} + +		newpw = strdup(newpw); +		if (newpw == NULL) { +			(void) fprintf(stderr, +			    gettext("Couldn't allocate memory\n")); +			exit(1); +		} +	} + +	if (nflag == 0) { +		code = krb5_set_password_using_ccache(ctx, cc, newpw, victim, +		    &result_code, &result_code_string, &result_string); +		if (code != 0) { +			com_err(whoami, code, +			    gettext("krb5_set_password() failed")); +			exit(1); +		} +		krb5_cc_close(ctx, cc); + +		(void) printf("Result: %.*s (%d) %.*s\n", +		    result_code == 0 ? +		    strlen("success") : result_code_string.length, +		    result_code == 0 ? "success" : result_code_string.data, +		    result_code, +		    result_string.length, result_string.data); + +		if (result_code != 0) { +			(void) fprintf(stderr, gettext("Exiting...\n")); +			exit(result_code); +		} +	} + +	if (enctype_count && (code = kt_remove_entries(ctx, kt, victim))) +		goto error; + +	for (i = 0; i < enctype_count; i++) +		kt_add_entry(ctx, kt, victim, enctypes[i], kvno, newpw); + +error: +	if (kt != NULL) +		krb5_kt_close(ctx, kt); + +	return (code ? 1 : 0); +} + +static +krb5_error_code +kt_remove_entries(krb5_context ctx, krb5_keytab kt, const krb5_principal princ) +{ +	krb5_error_code code; +	krb5_kt_cursor cursor; +	krb5_keytab_entry entry; + +	/* +	 * This is not a fatal error, we expect this to fail in the majority +	 * of cases (when clients are first initialized). +	 */ +	code = krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry); +	if (code != 0) { +		com_err(whoami, code, +		    gettext("Could not retrieve entry in keytab")); +		return (0); +	} + +	krb5_kt_free_entry(ctx, &entry); + +	code = krb5_kt_start_seq_get(ctx, kt, &cursor); +	if (code != 0) { +		com_err(whoami, code, gettext("While starting keytab scan")); +		return (code); +	} + +	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) { +		if (krb5_principal_compare(ctx, princ, entry.principal)) { + +			code = krb5_kt_end_seq_get(ctx, kt, &cursor); +			if (code != 0) { +				com_err(whoami, code, +				    gettext("While temporarily " +				    "ending keytab scan")); +				return (code); +			} + +			code = krb5_kt_remove_entry(ctx, kt, &entry); +			if (code != 0) { +				com_err(whoami, code, +				    gettext("While deleting entry " +				    "from keytab")); +				return (code); +			} + +			code = krb5_kt_start_seq_get(ctx, kt, &cursor); +			if (code != 0) { +				com_err(whoami, code, +				    gettext("While restarting keytab scan")); +				return (code); +			} +		} + +		krb5_kt_free_entry(ctx, &entry); +	} + +	if (code && code != KRB5_KT_END) { +		com_err(whoami, code, gettext("While scanning keytab")); +		return (code); +	} + +	if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor))) { +		com_err(whoami, code, gettext("While ending keytab scan")); +		return (code); +	} + +	return (0); +} + +static +void +kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ, +	krb5_enctype enctype, krb5_kvno kvno, const char *pw) +{ +	krb5_keytab_entry *entry; +	krb5_data password, salt; +	krb5_keyblock key; +	krb5_error_code code; +	char buf[100]; + +	if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) { +		com_err(whoami, code, gettext("Enctype %d has no name!"), +		    enctype); +		return; +	} +	if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) { +		(void) fprintf(stderr, gettext("Couldn't allocate memory")); +		return; +	} + +	(void) memset((char *)entry, 0, sizeof (*entry)); + +	password.length = strlen(pw); +	password.data = (char *)pw; + +	if ((code = krb5_principal2salt(ctx, princ, &salt)) != 0) { +		com_err(whoami, code, +		    gettext("Could not compute salt for %s"), enctype); +		return; +	} + +	code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key); + +	if (code != 0) { +		com_err(whoami, code, gettext("Could not compute salt for %s"), +		    enctype); +		krb5_xfree(salt.data); +		return; +	} + +	(void) memcpy(&entry->key, &key, sizeof (krb5_keyblock)); +	entry->vno = kvno; +	entry->principal = princ; + +	if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) { +		com_err(whoami, code, +		    gettext("Could not add entry to keytab")); +	} +} + +static +void +usage() +{ +	(void) fprintf(stderr, gettext("Usage: %s [-c ccache] [-k keytab] " +	    "[-e enctype_list] [-n] princ\n"), whoami); +	(void) fprintf(stderr, +	    gettext("\t-n\tDon't set the principal's password\n")); +	(void) fprintf(stderr, gettext("\tenctype_list is a comma or whitespace" +	    " separated list\n")); +	(void) fprintf(stderr, gettext("\tIf -n is used then -k and -e must be " +	    "used\n")); + +	exit(1); +} diff --git a/usr/src/cmd/krb5/kadmin/kclient/ksmb.c b/usr/src/cmd/krb5/kadmin/kclient/ksmb.c new file mode 100644 index 0000000000..0e39247e20 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/ksmb.c @@ -0,0 +1,131 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident	"%Z%%M%	%I%	%E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <locale.h> +#include <netdb.h> +#include <limits.h> +#include <smbsrv/libsmbns.h> + +#define	QUOTE(x)	#x +#define	VAL2STR(x)	QUOTE(x) + +char *whoami = NULL; + +static void usage(); + +static +void +usage() +{ +	fprintf(stderr, +	    gettext("Usage: %s [ -d fqdn ] [ -s server ]\n"), whoami); +	fprintf(stderr, +	    gettext("\t-d\tThe fully qualified domain of the client\n")); +	fprintf(stderr, gettext("\t-s\tThe domain controller to join\n")); +	fprintf(stderr, +	    gettext("\tstdin is used to read in the password or \n")); +	fprintf(stderr, gettext("\tthe password is prompted for.\n")); + +	exit(1); +} + +int +main(int argc, char **argv) +{ +	char c, fqdn[MAXHOSTNAMELEN], server[MAXHOSTNAMELEN]; +	char *newpw; +	int ret = 0; + +	(void) setlocale(LC_ALL, ""); + +#if !defined(TEXT_DOMAIN) +#define	TEXT_DOMAIN "SYS_TEST" +#endif /* TEXT_DOMAIN */ + +	(void) textdomain(TEXT_DOMAIN); + +	whoami = argv[0]; + +	while ((c = getopt(argc, argv, "d:s:")) != -1) { +		switch (c) { +		case 'd': +			(void) strncpy(fqdn, optarg, sizeof (fqdn)); +			break; +		case 's': +			(void) strncpy(server, optarg, sizeof (server)); +			break; +		default: +			usage(); +			break; +		} +	} + +	if (argc != optind) +		usage(); + +	if (!isatty(fileno(stdin))) { +		char buf[PASS_MAX + 1]; + +		if (scanf("%" VAL2STR(PASS_MAX) "s", &buf) != 1) { +			fprintf(stderr, +			    gettext("Couldn't read new password\n")); +			exit(1); +		} + +		newpw = strdup(buf); +		if (newpw == NULL) { +			fprintf(stderr, gettext("Couldn't allocate memory\n")); +			exit(1); +		} +	} else { +		newpw = getpassphrase(gettext("Enter new password: ")); +		if (newpw == NULL) { +			fprintf(stderr, +			    gettext("Couldn't read new password\n")); +			exit(1); +		} + +		newpw = strdup(newpw); +		if (newpw == NULL) { +			fprintf(stderr, gettext("Couldn't allocate memory\n")); +			exit(1); +		} +	} + +	/* +	 * Set the SMF properties for smb for later use. +	 */ +	ret = smb_setdomainprops(fqdn, server, newpw); + +	free(newpw); + +	return (ret); +} diff --git a/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_first b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_first new file mode 100644 index 0000000000..18526eea94 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_first @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved. +# Use is subject to license terms. +# +# ident	"%Z%%M%	%I%	%E% SMI" +# +# PAM include module for Kerberos authentication foremost and then fall-back +# to crypt authentication. +# +OTHER	auth requisite		pam_authtok_get.so.1 +OTHER	auth required		pam_dhkeys.so.1 +OTHER	auth required		pam_unix_cred.so.1 +OTHER	auth sufficient		pam_krb5.so.1 +OTHER	auth required		pam_unix_auth.so.1 diff --git a/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_only b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_only new file mode 100644 index 0000000000..70839899b7 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_only @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved. +# Use is subject to license terms. +# +# ident	"%Z%%M%	%I%	%E% SMI" +# +# PAM include module for Kerberos authentication foremost and then fall-back +# to crypt authentication. +# +OTHER	auth requisite		pam_authtok_get.so.1 +OTHER	auth required		pam_dhkeys.so.1 +OTHER	auth required		pam_unix_cred.so.1 +OTHER	auth binding		pam_krb5.so.1 +OTHER	auth required		pam_unix_auth.so.1 diff --git a/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_optional b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_optional new file mode 100644 index 0000000000..3c84d56f26 --- /dev/null +++ b/usr/src/cmd/krb5/kadmin/kclient/pam_krb5_optional @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved. +# Use is subject to license terms. +# +# ident	"%Z%%M%	%I%	%E% SMI" +# +# PAM include module for Kerberos authentication foremost and then fall-back +# to crypt authentication. +# +OTHER	auth requisite		pam_authtok_get.so.1 +OTHER	auth required		pam_dhkeys.so.1 +OTHER	auth required		pam_unix_cred.so.1 +OTHER	auth required		pam_unix_auth.so.1 +OTHER	auth optional		pam_krb5.so.1 diff --git a/usr/src/cmd/krb5/kadmin/kdcmgr/klookup.c b/usr/src/cmd/krb5/kadmin/kdcmgr/klookup.c index 4d8ceef881..206940d5d1 100644 --- a/usr/src/cmd/krb5/kadmin/kdcmgr/klookup.c +++ b/usr/src/cmd/krb5/kadmin/kdcmgr/klookup.c @@ -20,7 +20,7 @@   */  /* - * Copyright 2007 Sun Microsystems, Inc.  All rights reserved. + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms.   */ @@ -37,22 +37,64 @@  #include <unistd.h>  #include <ctype.h> +/* + * Private resolver of target and type with arguments: + * klooukp [ target [ RR_type ] ] + * + * Utitilizes DNS lookups to discover domain and realm information.  This CLI + * is used primarily by kdcmgr(1M) and kclient(1M). + */ +  int  /* ARGSUSED */  main(int argc, char **argv)  { -	unsigned char answer[NS_MAXMSG], *ansp = NULL, *end; +	unsigned char answer[NS_MAXMSG], *ansp = NULL, *end, a, b, c, d;  	int len = 0, anslen, hostlen, nq, na, type, class; -	char hostname[MAXHOSTNAMELEN], *cp; +	int ttl, priority, weight, port, size; +	char name[NS_MAXDNAME], *cp, *typestr = NULL; +	char nbuf[INET6_ADDRSTRLEN];  	struct __res_state stat;  	int found = 0; +	int rr_type = T_A;  	HEADER *h; -	if (argc != 1) +	if (argc > 3)  		exit(1); -	if (gethostname(hostname, MAXHOSTNAMELEN) != 0) -		exit(1); +	if (argc == 1) { +		if (gethostname(name, MAXHOSTNAMELEN) != 0) +			exit(1); +	} else { +		(void) strncpy(name, (char *)argv[1], NS_MAXDNAME); +		if (argc == 3) { +			typestr = argv[2]; + +			switch (*typestr) { +			case 'A': +				rr_type = T_A; +				break; +			case 'C': +				rr_type = T_CNAME; +				break; +			case 'I': +				rr_type = T_A; +				break; +			case 'P': +				rr_type = T_PTR; +				(void) sscanf(name, "%d.%d.%d.%d", +				    &a, &b, &c, &d); +				(void) sprintf(name, "%d.%d.%d.%d.in-addr.arpa", +				    d, c, b, a); +				break; +			case 'S': +				rr_type = T_SRV; +				break; +			default: +				exit(1); +			} +		} +	}  	(void) memset(&stat, 0, sizeof (stat)); @@ -60,10 +102,12 @@ main(int argc, char **argv)  		exit(1);  	anslen = sizeof (answer); -	len = res_nsearch(&stat, hostname, C_IN, T_A, answer, anslen); +	len = res_nsearch(&stat, name, C_IN, rr_type, answer, anslen); -	if (len < sizeof (HEADER)) +	if (len < sizeof (HEADER)) { +		res_ndestroy(&stat);  		exit(1); +	}  	ansp = answer;  	end = ansp + anslen; @@ -74,47 +118,86 @@ main(int argc, char **argv)  	na = ntohs(h->ancount);  	ansp += HFIXEDSZ; -	if (nq != 1 || na < 1) +	if (nq != 1 || na < 1) { +		res_ndestroy(&stat);  		exit(1); +	} -	hostlen = sizeof (hostname); -	len = dn_expand(answer, end, ansp, hostname, hostlen); -	if (len < 0) +	hostlen = sizeof (name); +	len = dn_expand(answer, end, ansp, name, hostlen); +	if (len < 0) { +		res_ndestroy(&stat);  		exit(1); +	}  	ansp += len + QFIXEDSZ; -	if (ansp > end) +	if (ansp > end) { +		res_ndestroy(&stat);  		exit(1); +	}  	while (na-- > 0 && ansp < end) { -		len = dn_expand(answer, end, ansp, hostname, hostlen); + +		len = dn_expand(answer, end, ansp, name, hostlen);  		if (len < 0)  			continue; -		ansp += len;			/* hostname */ -		type = ns_get16(ansp); -		ansp += INT16SZ;		/* type */ -		class = ns_get16(ansp); -		ansp += INT16SZ;		/* class */ -		ansp += INT32SZ;		/* ttl */ -		len = ns_get16(ansp); -		ansp += INT16SZ;		/* size */ +		ansp += len;			/* name */ +		NS_GET16(type, ansp);		/* type */ +		NS_GET16(class, ansp);		/* class */ +		NS_GET32(ttl, ansp);		/* ttl */ +		NS_GET16(size, ansp);		/* size */ + +		if ((ansp + size) > end) { +			res_ndestroy(&stat); +			exit(1); +		} +		if (type == T_SRV) { +			NS_GET16(priority, ansp); +			NS_GET16(weight, ansp); +			NS_GET16(port, ansp); +			len = dn_expand(answer, end, ansp, name, hostlen); +			if (len < 0) { +				res_ndestroy(&stat); +				exit(1); +			} +			for (cp = name; *cp; cp++) { +				*cp = tolower(*cp); +			} +			(void) printf("%s %d\n", name, port); +		} else if (typestr && *typestr == 'I') { +			(void) inet_ntop(AF_INET, (void *)ansp, nbuf, +			    INET6_ADDRSTRLEN); +			(void) strncpy(name, nbuf, MAXHOSTNAMELEN); +		} else if (type == T_PTR) { +			len = dn_expand(answer, end, ansp, name, hostlen); +			if (len < 0) { +				res_ndestroy(&stat); +				exit(1); +			} +		}  		ansp += len; -		if (type == T_A && class == C_IN) { +		if (type == rr_type && class == C_IN) {  			found = 1; -			break; +			if (type != T_SRV) +				break;  		}  	} -	if (found != 1) +	if (found != 1) { +		res_ndestroy(&stat);  		exit(1); +	} -	for (cp = hostname; *cp; cp++) { +	for (cp = name; *cp; cp++) {  		*cp = tolower(*cp);  	} -	(void) printf("%s\n", hostname); +	if (type != T_SRV) +		(void) printf("%s\n", name); + +	res_ndestroy(&stat);  	return (0);  } diff --git a/usr/src/lib/krb5/kadm5/clnt/client_init.c b/usr/src/lib/krb5/kadm5/clnt/client_init.c index be34483b9e..b7d186701e 100644 --- a/usr/src/lib/krb5/kadm5/clnt/client_init.c +++ b/usr/src/lib/krb5/kadm5/clnt/client_init.c @@ -1,5 +1,5 @@  /* - * Copyright 2007 Sun Microsystems, Inc.  All rights reserved. + * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.   * Use is subject to license terms.   *   * $Header: /cvs/krbdev/krb5/src/lib/kadm5/clnt/client_init.c,v 1.13.2.2 2000/05/09 13:17:14 raeburn Exp $ @@ -331,8 +331,18 @@ _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle,  	OM_uint32 gssstat, minor_stat;  	void *handlep;  	enum clnt_stat rpc_err_code; +	char *server = handle->params.admin_server; -	hp = gethostbyname(handle->params.admin_server); +	/* +	 * Try to find the kpasswd_server first if this is for the changepw +	 * service.  If defined then it should be resolvable else return error. +	 */ +	if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE,  +	    strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) { +		if (handle->params.kpasswd_server != NULL) +			server = handle->params.kpasswd_server; +	} +	hp = gethostbyname(server);  	if (hp == (struct hostent *)NULL) {  		code = KADM5_BAD_SERVER_NAME;  		ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, @@ -707,8 +717,12 @@ static kadm5_ret_t _kadm5_init_any(char *client_name,  #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \  			 KADM5_CONFIG_ADMIN_SERVER | \  			 KADM5_CONFIG_KADMIND_PORT)  +#define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ +			 KADM5_CONFIG_KPASSWD_SERVER | \ +			 KADM5_CONFIG_KPASSWD_PORT)  -     if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { +     if (((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) && +	 ((handle->params.mask & KPW_REQUIRED_PARAMS) != KPW_REQUIRED_PARAMS)) {  		(void) kadm5_free_config_params(handle->context,  						&handle->params);  	  krb5_free_context(handle->context); diff --git a/usr/src/lib/krb5/kadm5/kadm_host_srv_names.c b/usr/src/lib/krb5/kadm5/kadm_host_srv_names.c index bcc5cc8710..9abe0fe842 100644 --- a/usr/src/lib/krb5/kadm5/kadm_host_srv_names.c +++ b/usr/src/lib/krb5/kadm5/kadm_host_srv_names.c @@ -14,6 +14,7 @@  #include <os-proto.h>  #define	KADM5_MASTER "admin_server" +#define	KADM5_KPASSWD "kpasswd_server"  /*   * Find the admin server for the given realm. If the realm is null or @@ -67,6 +68,75 @@ kadm5_get_master(krb5_context context, const char *realm, char **master)  }  /* + * Find the kpasswd server for the given realm. If the realm is null or + * the empty string, find the admin server for the default realm. + * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to + * free the storage allocated to the admin server, master. + */ +kadm5_ret_t +kadm5_get_kpasswd(krb5_context context, const char *realm, char **kpasswd) +{ +	char *def_realm = NULL; +	char *delim; +#ifdef KRB5_DNS_LOOKUP +	struct sockaddr *addrs; +	int naddrs; +	unsigned short dns_portno; +	char dns_host[MAX_DNS_NAMELEN]; +	krb5_data dns_realm; +	krb5_error_code dns_ret = 1, ret; +#endif /* KRB5_DNS_LOOKUP */ + +	if (realm == 0 || *realm == '\0') { +		ret = krb5_get_default_realm(context, &def_realm); +		if (ret != 0) +			return (ret); +	} + +	(void) profile_get_string(context->profile, "realms", +	    realm ? realm : def_realm, +	    KADM5_KPASSWD, 0, kpasswd); + +	if ((*kpasswd != NULL) && ((delim = strchr(*kpasswd, ':')) != NULL)) +		*delim = '\0'; +#ifdef KRB5_DNS_LOOKUP +	if (*kpasswd == NULL) { +		/* +		 * Initialize realm info for (possible) DNS lookups. +		 */ +		dns_realm.data = strdup(realm ? realm : def_realm); +		if (dns_realm.data == NULL) { +			if (def_realm != NULL) +				free(def_realm); +			return (ENOMEM); +		} +		dns_realm.length = strlen(realm ? realm : def_realm); +		dns_realm.magic = 0; + +		dns_ret = krb5_get_servername(context, &dns_realm, +		    "_kpasswd", "_tcp", +		    dns_host, &dns_portno); +		if (dns_ret == 0) { +			*kpasswd = strdup(dns_host); + +			if (*kpasswd == NULL) { +				free(dns_realm.data); +				if (def_realm != NULL) +					free(def_realm); +				return (ENOMEM); +			} +		} + +		free(dns_realm.data); +	} +#endif /* KRB5_DNS_LOOKUP */ + +	if (def_realm != NULL) +		free(def_realm); +	return (*kpasswd ? KADM5_OK : KADM5_NO_SRV); +} + +/*   * Get the host base service name for the admin principal. Returns   * KADM5_OK on success. Caller must free the storage allocated for   * host_service_name. @@ -108,9 +178,14 @@ kadm5_get_cpw_host_srv_name(krb5_context context,  	char *name;  	char *host; - -	if (ret = kadm5_get_master(context, realm, &host)) -		return (ret); +	/* +	 * First try to find the kpasswd server, after all we are about to +	 * try to change our password.  If this fails then try admin_server. +	 */ +	if (ret = kadm5_get_kpasswd(context, realm, &host)) { +		if (ret = kadm5_get_master(context, realm, &host)) +			return (ret); +	}  	name = malloc(strlen(KADM5_CHANGEPW_HOST_SERVICE) + strlen(host) + 2);  	if (name == NULL) { diff --git a/usr/src/pkgdefs/SUNWkdcu/prototype_com b/usr/src/pkgdefs/SUNWkdcu/prototype_com index 074ea6ed31..ccfd32a17e 100644 --- a/usr/src/pkgdefs/SUNWkdcu/prototype_com +++ b/usr/src/pkgdefs/SUNWkdcu/prototype_com @@ -19,7 +19,7 @@  # CDDL HEADER END  #  # -# Copyright 2007 Sun Microsystems, Inc.  All rights reserved. +# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.  # Use is subject to license terms.  #  #ident	"%Z%%M%	%I%	%E% SMI" @@ -55,6 +55,7 @@ f none usr/lib/krb5/db2.so.1 755 root bin  s none usr/lib/krb5/db2.so=db2.so.1  f none usr/lib/krb5/gkadmin.jar 444 root bin  f none usr/lib/krb5/kadmind 500 root bin +f none usr/lib/krb5/kdyndns 555 root bin  f none usr/lib/krb5/kldap.so.1 755 root bin  f none usr/lib/krb5/klookup 555 root bin  s none usr/lib/krb5/kldap.so=kldap.so.1 @@ -62,6 +63,8 @@ f none usr/lib/krb5/kprop 555 root bin  f none usr/lib/krb5/kprop_script 555 root bin  f none usr/lib/krb5/kpropd 555 root bin  f none usr/lib/krb5/krb5kdc 500 root bin +f none usr/lib/krb5/ksetpw 555 root bin +f none usr/lib/krb5/ksmb 555 root bin  f none usr/lib/krb5/libdb2.so.1 755 root bin  s none usr/lib/krb5/libdb2.so=libdb2.so.1  f none usr/lib/krb5/libdyn.so.1 755 root bin @@ -75,6 +78,10 @@ s none usr/lib/krb5/libkdb.so=libkdb.so.1  f none usr/lib/krb5/libkdb_ldap.so.1 755 root bin  s none usr/lib/krb5/libkdb_ldap.so=libkdb_ldap.so.1  f none usr/lib/krb5/visualrt.jar 444 root bin +d none usr/lib/security 0755 root bin +f none usr/lib/security/pam_krb5_first 0444 root bin +f none usr/lib/security/pam_krb5_only 0444 root bin +f none usr/lib/security/pam_krb5_optional 0444 root bin  d none usr/sbin 0755 root bin  f none usr/sbin/gkadmin 555 root bin  f none usr/sbin/k5srvutil 555 root bin diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386 index e5639d762a..ad475347be 100644 --- a/usr/src/pkgdefs/etc/exception_list_i386 +++ b/usr/src/pkgdefs/etc/exception_list_i386 @@ -563,13 +563,16 @@ usr/lib/krb5/README.db2			i386  usr/lib/krb5/db2.so			i386  usr/lib/krb5/db2.so.1			i386  usr/lib/krb5/kadmind			i386 +usr/lib/krb5/kdyndns			i386  usr/lib/krb5/klookup			i386  usr/lib/krb5/kprop			i386  usr/lib/krb5/kprop_script		i386  usr/lib/krb5/kpropd			i386 -usr/lib/krb5/krb5kdc			i386  usr/lib/krb5/kldap.so			i386  usr/lib/krb5/kldap.so.1			i386 +usr/lib/krb5/krb5kdc			i386 +usr/lib/krb5/ksetpw			i386 +usr/lib/krb5/ksmb			i386  usr/lib/krb5/libdb2.so			i386  usr/lib/krb5/libdb2.so.1		i386  usr/lib/krb5/libdyn.so			i386 diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index e5cf37d9bb..a81d49e2c5 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -568,13 +568,16 @@ usr/lib/krb5/README.db2			sparc  usr/lib/krb5/db2.so			sparc  usr/lib/krb5/db2.so.1			sparc  usr/lib/krb5/kadmind			sparc +usr/lib/krb5/kdyndns			sparc  usr/lib/krb5/klookup			sparc  usr/lib/krb5/kprop			sparc  usr/lib/krb5/kprop_script		sparc  usr/lib/krb5/kpropd			sparc -usr/lib/krb5/krb5kdc			sparc  usr/lib/krb5/kldap.so			sparc  usr/lib/krb5/kldap.so.1			sparc +usr/lib/krb5/krb5kdc			sparc +usr/lib/krb5/ksetpw			sparc +usr/lib/krb5/ksmb			sparc  usr/lib/krb5/libdb2.so			sparc  usr/lib/krb5/libdb2.so.1		sparc  usr/lib/krb5/libdyn.so			sparc | 
