#!/bin/ksh93 -p # # 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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. # Copyright 2016 Toomas Soome # # This script is used to setup the Kerberos client by # supplying information about the Kerberos realm and kdc. # # The kerberos configuration file (/etc/krb5/krb5.conf) would # be generated and local host's keytab file setup. The script # can also optionally setup the system to do kerberized nfs and # bringover a master krb5.conf copy from a specified location. # proc cleanup { kdestroy -q > $TMP_FILE 2>&1 rm -r $TMPDIR > /dev/null 2>&1 exit $1 } proc exiting { printf "\n$(gettext "Exiting setup, nothing changed").\n\n" cleanup $1 } proc error_message { printf -- "---------------------------------------------------\n" >&2 printf "$(gettext "Setup FAILED").\n\n" >&2 cleanup 1 } proc check_bin { typeset bin=$1 if [[ ! -x $bin ]] { printf "$(gettext "Could not access/execute %s").\n" $bin >&2 error_message } } proc cannot_create { typeset filename="$1" typeset stat="$2" if [[ $stat -ne 0 ]] { printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2 error_message } } proc update_pam_conf { typeset PAM TPAM service setvar PAM = '/etc/pam.conf' setvar TPAM = $(mktemp -q -t kclient-pamconf.XXXXXX) if [[ -z $TPAM ]] { printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2 error_message } cp $PAM $TPAM >/dev/null 2>&1 printf "$(gettext "Configuring %s").\n\n" $PAM for service in [$SVCs] { setvar svc = ${service%:*} setvar auth_type = ${service#*:} if egrep -s "^$svc[ ][ ]*auth.*pam_krb5*" $TPAM { printf "$(gettext "The %s service is already configured for pam_krb5, please merge this service in %s").\n\n" $svc $PAM >&2 continue } else { exec 3>>$TPAM printf "\n$svc\tauth include\t\tpam_krb5_$auth_type\n" 1>&3 } } cp $TPAM $PAM > /dev/null 2>&1 rm $TPAM > /dev/null 2>&1 } proc modify_nfssec_conf { typeset NFSSEC_FILE="/etc/nfssec.conf" if [[ -r $NFSSEC_FILE ]] { cat $NFSSEC_FILE > $NFSSEC_FILE.sav cannot_create $NFSSEC_FILE.sav $Status } cat $NFSSEC_FILE > $TMP_FILE cannot_create $TMP_FILE $Status if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1 { sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE cannot_create $NFSSEC_FILE $Status } } proc call_kadmin { typeset svc="$1" typeset bool1 bool2 bool3 bool4 typeset service_princ getprincsubcommand anksubcommand ktaddsubcommand typeset ktremsubcommand for listentry in [$fqdnlist] { # Reset conditional vars to 1 setvar bool1 = '1'; setvar bool2 = '1'; setvar bool3 = '1'; setvar bool4 = '1' setvar service_princ = $(echo "${svc}/${listentry}") setvar getprincsubcommand = ""getprinc $service_princ"" setvar anksubcommand = ""addprinc -randkey $service_princ"" setvar ktaddsubcommand = ""ktadd $service_princ"" setvar ktremsubcommand = ""ktrem $service_princ all"" kadmin -c $KRB5CCNAME -q $getprincsubcommand 1>$TMP_FILE 2>&1 egrep -s $(gettext "get_principal: Principal does not exist") $TMP_FILE setvar bool1 = "$Status" egrep -s $(gettext "get_principal: Operation requires ``get") $TMP_FILE setvar bool2 = "$Status" if [[ $bool1 -eq 0 || $bool2 -eq 0 ]] { kadmin -c $KRB5CCNAME -q $anksubcommand 1>$TMP_FILE 2>&1 egrep -s $(gettext "add_principal: Principal or policy already exists while creating \"$service_princ@$realm\".") $TMP_FILE setvar bool3 = "$Status" egrep -s $(gettext "Principal \"$service_princ@$realm\" created.") $TMP_FILE setvar bool4 = "$Status" if [[ $bool3 -eq 0 || $bool4 -eq 0 ]] { printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ } else { cat $TMP_FILE; printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ >&2 error_message } } else { printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ >&2 } klist -k 1>$TMP_FILE 2>&1 egrep -s "$service_princ@$realm" $TMP_FILE if [[ $? -eq 0 ]] { 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 } 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 ]] { cat $TMP_FILE; printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ >&2 error_message } else { printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ } } } proc writeup_krb5_conf { typeset dh printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE exec 3>$KRB5_CONFIG if [[ $? -ne 0 ]] { printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2 error_message } printf "[libdefaults]\n" 1>&3 if [[ $no_keytab == yes ]] { printf "\tverify_ap_req_nofail = false\n" 1>&3 } if [[ $dns_lookup == yes ]] { printf "\t$dnsarg = on\n" 1>&3 if [[ $dnsarg == dns_lookup_kdc ]] { printf "\tdefault_realm = $realm\n" 1>&3 printf "\n[domain_realm]\n" 1>&3 if [[ -n $fkdc_list ]] { for kdc in [$fkdc_list] { printf "\t$kdc = $realm\n" 1>&3 } } printf "\t$FKDC = $realm\n" 1>&3 printf "\t$client_machine = $realm\n" 1>&3 if [[ -z $short_fqdn ]] { printf "\t.$domain = $realm\n\n" 1>&3 } else { printf "\t.$short_fqdn = $realm\n\n" 1>&3 } if [[ -n $domain_list ]] { for dh in [$domain_list] { printf "\t$dh = $realm\n" 1>&3 } } } else { if [[ $dnsarg = dns_lookup_realm ]] { printf "\tdefault_realm = $realm\n" 1>&3 printf "\n[realms]\n" 1>&3 printf "\t$realm = {\n" 1>&3 if [[ -n $kdc_list ]] { for kdc in [$kdc_list] { printf "\t\tkdc = $kdc\n" 1>&3 } } else { printf "\t\tkdc = $KDC\n" 1>&3 } printf "\t\tadmin_server = $KDC\n" 1>&3 if [[ $non_solaris == yes ]] { printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3 } printf "\t}\n\n" 1>&3 } else { printf "\tdefault_realm = $realm\n\n" 1>&3 } } } else { printf "\tdefault_realm = $realm\n\n" 1>&3 printf "[realms]\n" 1>&3 printf "\t$realm = {\n" 1>&3 if [[ -n $kdc_list ]] { for kdc in [$kdc_list] { printf "\t\tkdc = $kdc\n" 1>&3 } } else { printf "\t\tkdc = $KDC\n" 1>&3 } printf "\t\tadmin_server = $KDC\n" 1>&3 if [[ $non_solaris == yes ]] { printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3 } printf "\t}\n\n" 1>&3 printf "[domain_realm]\n" 1>&3 if [[ -n $fkdc_list ]] { for kdc in [$fkdc_list] { printf "\t$kdc = $realm\n" 1>&3 } } printf "\t$FKDC = $realm\n" 1>&3 printf "\t$client_machine = $realm\n" 1>&3 if [[ -z $short_fqdn ]] { printf "\t.$domain = $realm\n\n" 1>&3 } else { printf "\t.$short_fqdn = $realm\n\n" 1>&3 } if [[ -n $domain_list ]] { for dh in [$domain_list] { printf "\t$dh = $realm\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" 1>&3 if [[ $no_keytab == yes ]] { printf "\t\tno_addresses = true\n" 1>&3 } printf "\t}\n" 1>&3 } proc ask { typeset question=$1 typeset default_answer=$2 if [[ -z $default_answer ]] { printf "$question :" } else { printf "$question [$default_answer]: " } read answer test -z $answer && setvar answer = "$default_answer" } proc yesno { typeset question="$1" setvar answer = '' setvar yn = $(printf $(gettext "y/n")) setvar y = $(printf $(gettext "y")) setvar n = $(printf $(gettext "n")) setvar yes = $(printf $(gettext "yes")) setvar no = $(printf $(gettext "no")) { ask $question $yn match $answer { with $y|$yes setvar answer = 'yes' with $n|$no setvar answer = 'no' with * setvar answer = '' } } } proc query { yesno "$ifsjoin(ARGV)" if [[ $answer == no ]] { printf "\t$(gettext "No action performed").\n" } } proc read_profile { typeset param value typeset file="$1" if [[ ! -d $file && -r $file ]] { while read param value { match $param { with REALM if [[ -z $realm ]] { setvar realm = "$value" setvar checkval = ""REALM""; check_value $realm } with KDC if [[ -z $KDC ]] { setvar KDC = "$value" setvar checkval = ""KDC""; check_value $KDC } with ADMIN if [[ -z $ADMIN_PRINC ]] { setvar ADMIN_PRINC = "$value" setvar checkval = ""ADMIN_PRINC"" check_value $ADMIN_PRINC } with FILEPATH if [[ -z $filepath ]] { setvar filepath = "$value" } with NFS if [[ -z $add_nfs ]] { if [[ $value == 1 ]] { setvar add_nfs = 'yes' } else { setvar add_nfs = 'no' } } with NOKEY if [[ -z $no_keytab ]] { if [[ $value == 1 ]] { setvar no_keytab = 'yes' } else { setvar no_keytab = 'no' } } with NOSOL if [[ -z $non_solaris ]] { if [[ $value == 1 ]] { setvar non_solaris = 'yes' setvar no_keytab = 'yes' } else { setvar non_solaris = 'no' } } with LHN if [[ -z $logical_hn ]] { setvar logical_hn = "$value" setvar checkval = ""LOGICAL_HOSTNAME"" check_value $logical_hn } with DNSLOOKUP if [[ -z $dnsarg ]] { setvar dnsarg = "$value" setvar checkval = ""DNS_OPTIONS"" check_value $dnsarg } with FQDN if [[ -z $fqdnlist ]] { setvar fqdnlist = "$value" setvar checkval = ""FQDN"" check_value $fqdnlist verify_fqdnlist $fqdnlist } with MSAD if [[ -z $msad ]] { if [[ $value == 1 ]] { setvar msad = 'yes' setvar non_solaris = 'yes' } else { setvar msad = 'no' } } } } <$file } else { printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file >&2 error_message } } proc ping_check { typeset machine="$1" typeset string="$2" if ping $machine 2 > /dev/null 2>&1 { : } else { printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine >&2 error_message } # Output timesync warning if not using a profile, i.e. in # interactive mode. if [[ -z $profile && $string == KDC ]] { # It's difficult to sync up time with KDC esp. if in a # zone so just print a warning about KDC time sync. printf "\n$(gettext "Note, this system and the KDC's time must be within 5 minutes of each other for Kerberos to function").\n" >&2 printf "$(gettext "Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n" >&2 break } } proc check_value { typeset arg="$1" if [[ -z $arg ]] { printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval >&2 error_message } else { echo $arg > $TMP_FILE if egrep -s '[*$^#!]+' $TMP_FILE { printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval >&2 error_message } } } proc set_dns_value { typeset -l arg="$1" if [[ $arg == dns_lookup_kdc || $arg == dns_lookup_realm || $arg == dns_fallback ]] { setvar dns_lookup = 'yes' } else { if [[ $arg == none ]] { setvar dns_lookup = 'no' } else { printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" >&2 error_message } } } proc verify_kdcs { typeset k_list="$1" typeset -l kdc typeset list fqhn f_list setvar kdc_list = $(echo "$k_list" | sed 's/,/ /g') if [[ -z $k_list ]] { printf "\n$(gettext "At least one KDC should be listed").\n\n" >&2 usage } for kdc in [$k_list] { if [[ $kdc != $KDC ]] { setvar list = ""$list $kdc"" setvar fkdc = $($KLOOKUP $kdc) if ping $fkdc 2 > /dev/null { : } else { printf "\n$(gettext "%s %s is unreachable, no action performed").\n" "KDC" $fkdc >&2 } setvar f_list = ""$f_list $fkdc"" } } setvar fkdc_list = "$f_list" setvar kdc_list = "$list" } proc parse_service { typeset service_list=$1 setvar service_list = ${service_list//,/ } for service in [$service_list] { setvar svc = ${service%:} setvar auth_type = ${service#:} [[ -z $svc || -z $auth_type ]] && return print -- $svc $auth_type } } proc verify_fqdnlist { typeset list="$1" typeset -l hostname typeset -i count=1 typeset fqdnlist eachfqdn tmpvar fullhost setvar list = $(echo "$list" | tr -d " " | tr -d "\t") setvar hostname = $(uname -n | cut -d"." -f1) setvar fqdnlist = "$client_machine" setvar eachfqdn = $(echo "$list" | cut -d"," -f$count) if [[ -z $eachfqdn ]] { printf "\n$(gettext "If the -f option is used, at least one FQDN should be listed").\n\n" >&2 usage } else { { setvar tmpvar = $(echo "$eachfqdn" | cut -d"." -f1) if [[ -z $tmpvar ]] { setvar fullhost = ""$hostname$eachfqdn"" } else { setvar fullhost = ""$hostname.$eachfqdn"" } ping_check $fullhost $(gettext "System") if [[ $fullhost == $client_machine ]] { : } else { setvar fqdnlist = ""$fqdnlist $fullhost"" } if [[ $list == *,* ]] { ((count = count + 1)) setvar eachfqdn = $(echo "$list" | cut -d"," -f$count) } else { break } } } } proc setup_keytab { typeset cname ask_fqdns current_release # # 1. kinit with ADMIN_PRINC # if [[ -z $ADMIN_PRINC ]] { printf "\n$(gettext "Enter the krb5 administrative principal to be used"): " read ADMIN_PRINC setvar checkval = ""ADMIN_PRINC""; check_value $ADMIN_PRINC } echo $ADMIN_PRINC>$TMP_FILE [[ -n $msad ]] && return if egrep -s '\/admin' $TMP_FILE { # Already in "/admin" format, do nothing : } else { if egrep -s '\/' $TMP_FILE { printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n" >&2 error_message } else { setvar ADMIN_PRINC = $(echo "$ADMIN_PRINC/admin") } } printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC setvar cname = $(canon_resolve $KDC) if [[ -n $cname ]] { kinit -S kadmin/$cname $ADMIN_PRINC } else { kinit -S kadmin/$FKDC $ADMIN_PRINC } klist 1>$TMP_FILE 2>&1 if egrep -s $(gettext "Valid starting") $TMP_FILE && egrep -s "kadmin/$FKDC@$realm" $TMP_FILE { : } else { printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC >&2 error_message } # # 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 ]] { query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?" setvar ask_fqdns = "$answer" if [[ $ask_fqdns == yes ]] { printf "$(gettext "Enter a comma-separated list of DNS domain names"): " read fqdnlist verify_fqdnlist $fqdnlist } else { setvar fqdnlist = "$client_machine" } } else { if [[ -z $fqdnlist ]] { setvar fqdnlist = "$client_machine" } } if [[ $add_nfs == yes ]] { echo; call_kadmin nfs } # Add the host entry to the keytab echo; call_kadmin host } proc setup_lhn { typeset -l logical_hn echo $logical_hn > $TMP_FILE if egrep -s '[^.]\.[^.]+$' $TMP_FILE { # do nothing, logical_hn is in fqdn format : } else { if egrep -s '\.+' $TMP_FILE { 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 setvar logical_hn = $(echo "$logical_hn.$fqdn") } } setvar client_machine = "$logical_hn" ping_check $client_machine $(gettext "System") } proc 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 } proc discover_domain { typeset dom DOMs if [[ -z $realm ]] { set -A DOMs -- $($KLOOKUP _ldap._tcp.dc._msdcs S) } else { set -A DOMs -- $($KLOOKUP _ldap._tcp.dc._msdcs.$realm S) } [[ -z ${DOMs[0]} ]] && return 1 setvar dom = ${DOMs[0]} setvar dom = ${dom#*.} setvar dom = ${dom% *} setvar domain = "$dom" return 0 } proc check_nss_hosts_or_ipnodes_config { typeset backend for backend in [$1] { [[ $backend == dns ]] && return 0 } return 1 } proc check_nss_conf { typeset i j hosts_config for i in [hosts ipnodes] { grep "^${i}:" /etc/nsswitch.conf|read j hosts_config check_nss_hosts_or_ipnodes_config $hosts_config || return 1 } return 0 } proc canon_resolve { typeset name ip setvar name = $($KLOOKUP $1 C) [[ -z $name ]] && setvar name = $($KLOOKUP $1 A) [[ -z $name ]] && return setvar ip = $($KLOOKUP $name I) [[ -z $ip ]] && return for i in [$ip] { if ping $i 2 > /dev/null 2>&1 { break } else { setvar i = '' } } setvar cname = $($KLOOKUP $ip P) [[ -z $cname ]] && return print -- $cname } proc rev_resolve { typeset name ip setvar ip = $($KLOOKUP $1 I) [[ -z $ip ]] && return setvar name = $($KLOOKUP $ip P) [[ -z $name ]] && return print -- $name } # Convert an AD-style domain DN to a DNS domainname proc dn2dns { typeset OIFS dname dn comp components setvar dn = "$1" setvar dname = '' setvar OIFS = "$IFS" setvar IFS = ',' set -A components -- $1 setvar IFS = "$OIFS" for comp in [${components[@]}] { [[ "$comp" == [dD][cC]=* ]] || continue setvar dname = ""$dname.${comp#??=}"" } print ${dname#.} } # Form a base DN from a DNS domainname and container proc getBaseDN { if [[ -n "$2" ]] { setvar baseDN = ""CN=$1,$(dns2dn $2)"" } else { setvar baseDN = "$(dns2dn $2)" } } # Convert a DNS domainname to an AD-style DN for that domain proc dns2dn { typeset OIFS dn labels setvar OIFS = "$IFS" setvar IFS = '.' set -A labels -- $1 setvar IFS = "$OIFS" setvar dn = '' for label in [${labels[@]}] { setvar dn = ""${dn},DC=$label"" } print -- ${dn#,} } proc getSRVs { typeset srv port $KLOOKUP $1 S | while read srv port { if ping $srv 2 > /dev/null 2>&1 { print -- $srv $port } } } proc getKDC { typeset j set -A KPWs -- $(getSRVs _kpasswd._tcp.$dom.) setvar kpasswd = ${KPWs[0]} if [[ -n $siteName ]] { set -A KDCs -- $(getSRVs _kerberos._tcp.$siteName._sites.$dom.) setvar kdc = ${KDCs[0]} [[ -n $kdc ]] && return } # No site name set -A KDCs -- $(getSRVs _kerberos._tcp.$dom.) setvar kdc = ${KDCs[0]} [[ -n $kdc ]] && return # Default set -A KDCs -- $DomainDnsZones 88 setvar kdc = "$ForestDnsZones" } proc getDC { typeset j if [[ -n $siteName ]] { set -A DCs -- $(getSRVs _ldap._tcp.$siteName._sites.dc._msdcs.$dom.) setvar dc = ${DCs[0]} [[ -n $dc ]] && return } # No site name set -A DCs -- $(getSRVs _ldap._tcp.dc._msdcs.$dom.) setvar dc = ${DCs[0]} [[ -n $dc ]] && return # Default set -A DCs -- $DomainDnsZones 389 setvar dc = "$DomainDnsZones" } proc write_ads_krb5conf { typeset kdcs printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE for i in [${KDCs[@]}] { [[ $i == +([0-9]) ]] && continue if [[ -n $kdcs ]] { setvar kdcs = ""$kdcs,$i"" } else { setvar kdcs = "$i" } } $KCONF -f $KRB5_CONFIG -r $realm -k $kdcs -m $KDC -p SET_CHANGE -d .$dom if [[ $? -ne 0 ]] { printf "\n$(gettext "Can not update %s, exiting").\n" $KRB5_CONFIG >&2 error_message } } proc getForestName { ldapsearch -R -T -h $dc $ldap_args \ -b "" -s base "" schemaNamingContext| \ grep ^schemaNamingContext|read j schemaNamingContext if [[ $? -ne 0 ]] { printf "$(gettext "Can't find forest").\n" >&2 error_message } setvar schemaNamingContext = ${schemaNamingContext#CN=Schema,CN=Configuration,} [[ -z $schemaNamingContext ]] && return 1 setvar forest = '' while [[ -n $schemaNamingContext ]] { setvar schemaNamingContext = ${schemaNamingContext#DC=} setvar forest = "${forest}.${schemaNamingContext%%,*}" [[ "$schemaNamingContext" = *,* ]] || break setvar schemaNamingContext = ${schemaNamingContext#*,} } setvar forest = ${forest#.} } proc getGC { typeset j [[ -n $gc ]] && return 0 if [[ -n $siteName ]] { set -A GCs -- $(getSRVs _ldap._tcp.$siteName._sites.gc._msdcs.$forest.) setvar gc = ${GCs[0]} [[ -n $gc ]] && return } # No site name set -A GCs -- $(getSRVs _ldap._tcp.gc._msdcs.$forest.) setvar gc = ${GCs[0]} [[ -n $gc ]] && return # Default set -A GCs -- $ForestDnsZones 3268 setvar gc = "$ForestDnsZones" } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # proc ipAddr2num { typeset OIFS typeset -ui16 num if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]] { print 0 return 0 } setvar OIFS = "$IFS" setvar IFS = '.' set -- $1 setvar IFS = "$OIFS" setvar num = $((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4})) print -- $num } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # proc num2ipAddr { typeset -ui16 num typeset -ui10 a b c d setvar num = "$1" setvar a = $((num>>24 )) setvar b = $((num>>16 & 16#ff)) setvar c = $((num>>8 & 16#ff)) setvar d = $((num & 16#ff)) print -- $a.$b.$c.$d } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # proc netmask2length { typeset -ui16 netmask typeset -i len setvar netmask = "$1" setvar len = '32' while [[ $((netmask % 2)) -eq 0 ]] { setvar netmask = $((netmask>>1)) setvar len = $((len - 1)) } print $len } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # proc getSubnets { typeset -ui16 addr netmask typeset -ui16 classa=16'#'ff000000 typeset -ui16 classb=16'#'ffff0000 typeset -ui16 classc=16'#'ffffff00 ifconfig -a|while read line { setvar addr = '0' setvar netmask = '0' set -- $line [[ $1 == inet ]] || continue while [[ $# -gt 0 ]] { match $1 { with inet setvar addr = $(ipAddr2num $2); shift with netmask eval netmask=16'#'$2; shift with * :; } shift } [[ $addr -eq 0 || $netmask -eq 0 ]] && continue [[ $((addr & classa)) -eq 16\#7f000000 ]] && continue print $(num2ipAddr $((addr & netmask)))/$(netmask2length $netmask) if test $netmask -gt $classc { print $(num2ipAddr $((addr & classc)))/$(netmask2length $classc) } if test $netmask -gt $classb { print $(num2ipAddr $((addr & classb)))/$(netmask2length $classb) } if test $netmask -gt $classa { print $(num2ipAddr $((addr & classa)))/$(netmask2length $classa) } } } proc getSite { typeset subnet siteDN j ldapsrv subnet_dom eval "[[ -n \"\$siteName\" ]]" && return for subnet in [$(getSubnets)] { ldapsearch -R -T -h $dc $ldap_args \ -p 3268 -b "" -s sub cn=$subnet dn |grep ^dn|read j subnetDN [[ -z $subnetDN ]] && continue setvar subnet_dom = $(dn2dns $subnetDN) setvar ldapsrv = $(canon_resolve DomainDnsZones.$subnet_dom) [[ -z $ldapsrv ]] && continue 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 } } proc doKRB5config { [[ -f $KRB5_CONFIG_FILE ]] && \ cp $KRB5_CONFIG_FILE ${KRB5_CONFIG_FILE}-pre-kclient [[ -f $KRB5_KEYTAB_FILE ]] && \ cp $KRB5_KEYTAB_FILE ${KRB5_KEYTAB_FILE}-pre-kclient [[ -s $KRB5_CONFIG ]] && cp $KRB5_CONFIG $KRB5_CONFIG_FILE [[ -s $KRB5_CONFIG_FILE ]] && chmod 0644 $KRB5_CONFIG_FILE [[ -s $new_keytab ]] && cp $new_keytab $KRB5_KEYTAB_FILE [[ -s $KRB5_KEYTAB_FILE ]] && chmod 0600 $KRB5_KEYTAB_FILE } proc addDNSRR { setvar smbFMRI = "svc:/network/smb/server:default" setvar ddnsProp = 'smbd/ddns_enable' setvar enProp = 'general/enabled' setvar enabled = $(svcprop -p $enProp $smbFMRI) setvar ddns_enable = $(svcprop -p $ddnsProp $smbFMRI) if [[ $enabled == true && $ddns_enable != true ]] { 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 } # 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 ]] { # # Non-fatal, we should carry-on as clients may resolve to # different servers and the client could already exist there. # printf "$(gettext "Warning: unable to create DNS records for client").\n" printf "$(gettext "This could mean that '%s' is not included as a 'nameserver' in the /etc/resolv.conf file or some other type of error").\n" $dc } } proc setSMB { typeset domain=$1 typeset server=$2 setvar smbFMRI = "svc:/network/smb/server" printf "%s" $newpw | $KSMB -d $domain -s $server if [[ $? -ne 0 ]] { printf "$(gettext "Warning: unable to set %s domain, server and password information").\n" $smbFMRI return } svcadm restart $smbFMRI > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Warning: unable to restart %s").\n" $smbFMRI } } proc compareDomains { typeset oldDom hspn newDom=$1 # If the client has been previously configured in a different # realm/domain then we need to prompt the user to see if they wish to # switch domains. klist -k 2>&1 | grep @ | read j hspn [[ -z $hspn ]] && return setvar oldDom = ${hspn#*@} if [[ $oldDom != $newDom ]] { 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 ]] { printf "$(gettext "Client will not be joined to the new domain").\n" >&2 error_message } } } proc getKDCDC { getKDC if [[ -n $kdc ]] { setvar KDC = "$kdc" setvar dc = "$kdc" } else { getDC if [[ -n $dc ]] { setvar KDC = "$dc" } else { printf "$(gettext "Could not find domain controller server for '%s'. Exiting").\n" $realm >&2 error_message } } } proc gen_rand { typeset -u hex dd if=/dev/random bs=1 count=1 2>/dev/null | od -A n -tx1 | read hex printf %s $((16#$hex)) } proc join_domain { typeset -u upcase_nodename typeset -l locase_nodename typeset -L15 string15 typeset netbios_nodename fqdn setvar container = 'Computers' setvar ldap_args = ""-o authzid= -o mech=gssapi"" setvar userAccountControlBASE = '4096' if [[ -z $ADMIN_PRINC ]] { setvar cprinc = 'Administrator' } else { setvar cprinc = "$ADMIN_PRINC" } if ! discover_domain { printf "$(gettext "Can not find realm") '%s'.\n" $realm >&2 error_message } setvar dom = "$domain" setvar realm = "$domain" if [[ ${#hostname} -gt 15 ]] { setvar string15 = "$hostname" setvar upcase_nodename = "$string15" setvar locase_nodename = "$string15" } else { setvar upcase_nodename = "$hostname" setvar locase_nodename = "$hostname" } setvar netbios_nodename = ""${upcase_nodename}"\$" setvar fqdn = "$hostname.$domain" setvar upn = "host/${fqdn}@${realm}" setvar object = $(mktemp -q -t kclient-computer-object.XXXXXX) if [[ -z $object ]] { printf "\n$(gettext "Can not create temporary file, exiting").\n " >&2 error_message } setvar modify_existing = 'false' setvar recreate = 'false' setvar DomainDnsZones = $(rev_resolve DomainDnsZones.$dom.) setvar ForestDnsZones = $(rev_resolve ForestDnsZones.$dom.) getBaseDN $container $dom if [[ -n $KDC ]] { setvar dc = "$KDC" } else { getKDCDC } write_ads_krb5conf printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm kinit $cprinc@$realm if [[ $? -ne 0 ]] { printf "$(gettext "Could not authenticate %s. Exiting").\n" $cprinc@$realm >&2 error_message } if getForestName { 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" } getGC getSite if [[ -z $siteName ]] { 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 } if [[ ${#GCs} -eq 0 ]] { printf "$(gettext "Could not find global catalogs. Exiting").\n" >&2 error_message } # 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 setvar level = '0' ldapsearch -R -T -h $dc $ldap_args -b "" -s base "" \ domainControllerFunctionality| grep ^domainControllerFunctionality| \ read j level if [[ $? -ne 0 ]] { printf "$(gettext "Search for domain functionality failed, exiting").\n" >&2 error_message } if ldapsearch -R -T -h $dc $ldap_args -b $baseDN \ -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1 { : } else { printf "$(gettext "Search for node failed, exiting").\n" >&2 error_message } ldapsearch -R -T -h $dc $ldap_args -b $baseDN -s sub \ sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn if [[ -z $dn ]] { : # 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 ]] { setvar recreate = 'true' } else { setvar modify_existing = 'true' } } if [[ $modify_existing == false && -n $dn ]] { query "$(gettext "Would you like to delete any sub-object found for this computer account") ?" if [[ $answer == yes ]] { 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 { [[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue if $recreate { 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 ]] { printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} } } else { printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn} } } } if $recreate { ldapdelete -h $dc $ldap_args $dn > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} >&2 error_message } } elif $modify_existing { : # Nothing to delete } else { printf "$(gettext "A machine account already exists").\n" >&2 error_message } } [[ -z $dn ]] && setvar dn = ""CN=${upcase_nodename},${baseDN}"" if $modify_existing { cat > "$object" <<< """ dn: $dn changetype: modify replace: userPrincipalName userPrincipalName: $upn - replace: servicePrincipalName servicePrincipalName: host/${fqdn} - replace: userAccountControl userAccountControl: $((userAccountControlBASE + 32 + 2)) - replace: dNSHostname dNSHostname: ${fqdn} """ printf "$(gettext "A machine account already exists; updating it").\n" ldapadd -h $dc $ldap_args -f $object > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Failed to modify the AD object via LDAP").\n" >&2 error_message } } else { setvar dn = ""CN=${upcase_nodename},${baseDN}"" cat > "$object" <<< """ dn: $dn objectClass: computer cn: $upcase_nodename sAMAccountName: ${netbios_nodename} userPrincipalName: $upn servicePrincipalName: host/${fqdn} userAccountControl: $((userAccountControlBASE + 32 + 2)) dNSHostname: ${fqdn} """ 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 ]] { printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2 error_message } } # Generate a new password for the new account setvar MAX_PASS = '120' setvar i = '0' # first check to see if /dev/random exists to generate a new password if [[ ! -e /dev/random ]] { printf "$(gettext "/dev/random does not exist").\n" >&2 error_message } while ((MAX_PASS > i)) { # [MS-DISO] A machine password is an ASCII string of randomly # chosen characters. Each character's ASCII code is between 32 # and 122 inclusive. setvar c = $(printf "\\$(printf %o $(($(gen_rand) % 91 + 32)))\n") setvar p = ""$p$c"" ((i+=1)) } setvar newpw = "$p" if [[ ${#newpw} -ne MAX_PASS ]] { printf "$(gettext "Password created was of incorrect length").\n" >&2 error_message } # Set the new password printf "%s" $newpw | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Failed to set account password").\n" >&2 error_message } # 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 ]] && setvar kvno = '1' # Set supported enctypes. This only works for Longhorn/Vista, so we # ignore errors here. setvar userAccountControl = $((userAccountControlBASE + 524288 + 65536)) set -A enctypes -- # Do we have local support for AES? encrypt -l|grep ^aes|read j minkeysize maxkeysize setvar val = '' if [[ $maxkeysize -eq 256 ]] { setvar val = '16' setvar enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96 } if [[ $minkeysize -eq 128 ]] { ((val=val+8)) setvar enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96 } # RC4 comes next (whether it's better than 1DES or not -- AD prefers it) if encrypt -l|grep -q ^arcfour { ((val=val+4)) setvar enctypes[${#enctypes[@]}]=arcfour-hmac-md5 } else { # Use 1DES ONLY if we don't have arcfour setvar userAccountControl = $((userAccountControl + 2097152)) } if encrypt -l | grep -q ^des { ((val=val+2)) setvar enctypes[${#enctypes[@]}]=des-cbc-md5 } if [[ ${#enctypes[@]} -eq 0 ]] { printf "$(gettext "No enctypes are supported").\n" printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(1M)").\n" >&2 error_message } # If domain crontroller is Longhorn or above then set new supported # encryption type attributes. if [[ $level -gt 2 ]] { cat > "$object" <<< """ dn: $dn changetype: modify replace: msDS-SupportedEncryptionTypes msDS-SupportedEncryptionTypes: $val """ ldapmodify -h $dc $ldap_args -f $object >/dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n" } } # 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" <<< """ dn: $dn changetype: modify replace: userAccountControl userAccountControl: $userAccountControl """ ldapmodify -h $dc $ldap_args -f $object >/dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2 error_message } # Setup a keytab file set -A args -- for enctype in [${enctypes[@]}] { setvar args[${#args[@]}]=-e setvar args[${#args[@]}]=$enctype } rm $new_keytab > /dev/null 2>&1 cat > "$object" <<< """ dn: $dn changetype: modify add: servicePrincipalName servicePrincipalName: nfs/${fqdn} servicePrincipalName: HTTP/${fqdn} servicePrincipalName: root/${fqdn} servicePrincipalName: cifs/${fqdn} servicePrincipalName: host/${upcase_nodename} """ ldapmodify -h $dc $ldap_args -f $object >/dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2 error_message } # # In Windows, unlike MIT based implementations we salt the keys with # the UPN, which is based on the host/string15@realm elements, not # with the individual SPN strings. # setvar salt = "host/${locase_nodename}.${domain}@${realm}" setvar skeys = ''(host/${fqdn}@${realm} nfs/${fqdn}@${realm} HTTP/${fqdn}@${realm}) setvar skeys = ''(root/${fqdn}@${realm} cifs/${fqdn}@${realm}) setvar skeys = ''(${netbios_nodename}@${realm} host/${upcase_nodename}@${realm}) setvar skeys = ''(cifs/${upcase_nodename}@${realm}) setvar ks_args = ""-n -s $salt -v $kvno -k $new_keytab ${args[@]}"" for skey in [${skeys[@]}] { printf "%s" $newpw | $KSETPW $ks_args $skey > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "$(gettext "Failed to set password").\n" >&2 error_message } } doKRB5config addDNSRR $dom setSMB $dom $dc printf -- "---------------------------------------------------\n" printf "$(gettext "Setup COMPLETE").\n\n" kdestroy -q 1>$TMP_FILE 2>&1 rm -f $TMP_FILE rm -rf $TMPDIR > /dev/null 2>&1 exit 0 } ########################### # Main section # ########################### # # Set the Kerberos config file and some default strings/files # setvar KRB5_CONFIG_FILE = '/etc/krb5/krb5.conf' setvar KRB5_KEYTAB_FILE = '/etc/krb5/krb5.keytab' setvar RESOLV_CONF_FILE = '/etc/resolv.conf' setvar KLOOKUP = '/usr/lib/krb5/klookup'; check_bin $KLOOKUP setvar KSETPW = '/usr/lib/krb5/ksetpw'; check_bin $KSETPW setvar KSMB = '/usr/lib/krb5/ksmb'; check_bin $KSMB setvar KDYNDNS = '/usr/lib/krb5/kdyndns'; check_bin $KDYNDNS setvar KCONF = '/usr/lib/krb5/kconf'; check_bin $KCONF setvar dns_lookup = 'no' setvar ask_fqdns = 'no' setvar adddns = 'no' setvar no_keytab = 'no' setvar checkval = """" setvar profile = """" typeset -u realm typeset -l hostname KDC export TMPDIR="/var/run/kclient" mkdir $TMPDIR > /dev/null 2>&1 if [[ $? -ne 0 ]] { printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2 exit 1 } setvar TMP_FILE = $(mktemp -q -t kclient-tmpfile.XXXXXX) export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX) export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) setvar new_keytab = $(mktemp -q -t kclient-krb5keytab.XXXXXX) if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]] { printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2 exit 1 } # # If we are interrupted, cleanup after ourselves # trap "exiting 1" HUP INT QUIT TERM if [[ -d /usr/bin ]] { if [[ -d /usr/sbin ]] { setvar PATH = "/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH" export PATH } else { printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2 exit 1 } } else { printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2 exit 1 } printf "\n$(gettext "Starting client setup")\n\n" printf -- "---------------------------------------------------\n" # # Check for uid 0, disallow otherwise # id 1>$TMP_FILE 2>&1 if [[ $? -eq 0 ]] { if egrep -s "uid=0\(root\)" $TMP_FILE { # uid is 0, go ahead ... : } else { printf "\n$(gettext "Administrative privileges are required to run this script, exiting").\n" >&2 error_message } } else { cat $TMP_FILE; printf "\n$(gettext "uid check failed, exiting").\n" >&2 error_message } setvar uname = $(uname -n) setvar hostname = ${uname%%.*} # # Process the command-line arguments (if any) # setvar OPTIND = '1' while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T: OPTIONS { match $OPTIONS { with D setvar options = ""$options -D"" setvar domain_list = "$OPTARG" with K setvar options = ""$options -K"" setvar no_keytab = 'yes' with R setvar options = ""$options -R"" setvar realm = "$OPTARG" setvar checkval = ""REALM""; check_value $realm with T setvar options = ""$options -T"" setvar type = "$OPTARG" if [[ $type == ms_ad ]] { setvar msad = 'yes' setvar adddns = 'yes' } else { setvar non_solaris = 'yes' setvar no_keytab = 'yes' } with a setvar options = ""$options -a"" setvar ADMIN_PRINC = "$OPTARG" setvar checkval = ""ADMIN_PRINC""; check_value $ADMIN_PRINC with c setvar options = ""$options -c"" setvar filepath = "$OPTARG" with d setvar options = ""$options -d"" setvar dnsarg = "$OPTARG" setvar checkval = ""DNS_OPTIONS""; check_value $dnsarg with f setvar options = ""$options -f"" setvar fqdnlist = "$OPTARG" with h setvar options = ""$options -h"" setvar logical_hn = "$OPTARG" setvar checkval = ""LOGICAL_HOSTNAME""; check_value $logical_hn with k setvar options = ""$options -k"" setvar kdc_list = "$OPTARG" with m setvar options = ""$options -m"" setvar KDC = "$OPTARG" setvar checkval = ""KDC""; check_value $KDC with n setvar options = ""$options -n"" setvar add_nfs = 'yes' with p setvar options = ""$options -p"" setvar profile = "$OPTARG" read_profile $profile with s setvar options = ""$options -s"" setvar svc_list = "$OPTARG" setvar SVCs = ${svc_list//,/ } with \? usage with * usage } } #correct argument count after options shift $(expr $OPTIND - 1) if [[ -z $options ]] { : } else { if [[ $# -ne 0 ]] { usage } } # # Check to see if we will be a client of a MIT, Heimdal, Shishi, etc. # if [[ -z $options ]] { query "$(gettext "Is this a client of a non-Solaris KDC") ?" setvar non_solaris = "$answer" if [[ $non_solaris == yes ]] { printf "$(gettext "Which type of KDC is the server"):\n" printf "\t$(gettext "ms_ad: Microsoft Active Directory")\n" printf "\t$(gettext "mit: MIT KDC server")\n" printf "\t$(gettext "heimdal: Heimdal KDC server")\n" printf "\t$(gettext "shishi: Shishi KDC server")\n" printf "$(gettext "Enter required KDC type"): " read kdctype if [[ $kdctype == ms_ad ]] { setvar msad = 'yes' } elif [[ $kdctype == mit || $kdctype == heimdal || \ $kdctype == shishi ]] { setvar no_keytab = 'yes' } else { printf "\n$(gettext "Invalid KDC type option, valid types are ms_ad, mit, heimdal, or shishi, exiting").\n" >&2 error_message } } } [[ $msad == yes ]] && join_domain # # Check for /etc/resolv.conf # if [[ -r $RESOLV_CONF_FILE ]] { setvar client_machine = $($KLOOKUP) if [[ $? -ne 0 ]] { if [[ $adddns == no ]] { printf "\n$(gettext "%s does not have a DNS record and is required for Kerberos setup")\n" $hostname >&2 error_message } } else { # # If client entry already exists then do not recreate it # setvar adddns = 'no' setvar hostname = ${client_machine%%.*} setvar domain = ${client_machine#*.} } setvar short_fqdn = ${domain#*.*} setvar 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 } check_nss_conf || printf "$(gettext "/etc/nsswitch.conf does not make use of DNS for hosts and/or ipnodes").\n" [[ -n $fqdnlist ]] && verify_fqdnlist $fqdnlist if [[ -z $dnsarg && (-z $options || -z $filepath) ]] { query "$(gettext "Do you want to use DNS for kerberos lookups") ?" if [[ $answer == yes ]] { 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 setvar checkval = ""DNS_OPTIONS""; check_value $dnsarg set_dns_value $dnsarg } } else { [[ -z $dnsarg ]] && setvar dnsarg = 'none' set_dns_value $dnsarg } if [[ -n $kdc_list ]] { if [[ -z $KDC ]] { for kdc in [$kdc_list] { break } setvar KDC = "$kdc" } } if [[ -z $realm ]] { printf "$(gettext "Enter the Kerberos realm"): " read realm setvar checkval = ""REALM""; check_value $realm } if [[ -z $KDC ]] { printf "$(gettext "Specify the master KDC hostname for the above realm"): " read KDC setvar checkval = ""KDC""; check_value $KDC } setvar FKDC = $($KLOOKUP $KDC) # # Ping to see if the kdc is alive ! # ping_check $FKDC "KDC" if [[ -z $kdc_list && (-z $options || -z $filepath) ]] { query "$(gettext "Do you have any slave KDC(s)") ?" if [[ $answer == yes ]] { printf "$(gettext "Enter a comma-separated list of slave KDC host names"): " read kdc_list } } [[ -n $kdc_list ]] && verify_kdcs $kdc_list # # Check to see if we will have a dynamic presence in the realm # if [[ -z $options ]] { query "$(gettext "Will this client need service keys") ?" if [[ $answer == no ]] { setvar no_keytab = 'yes' } } # # Check to see if we are configuring the client to use a logical host name # of a cluster environment # if [[ -z $options ]] { query "$(gettext "Is this client a member of a cluster that uses a logical host name") ?" if [[ $answer == yes ]] { printf "$(gettext "Specify the logical hostname of the cluster"): " read logical_hn setvar checkval = ""LOGICAL_HOSTNAME""; check_value $logical_hn setup_lhn } } if [[ -n $domain_list && (-z $options || -z $filepath) ]] { query "$(gettext "Do you have multiple domains/hosts to map to realm %s" ) ?" $realm if [[ $answer == yes ]] { printf "$(gettext "Enter a comma-separated list of domain/hosts to map to the default realm"): " read domain_list } } [[ -n domain_list ]] && setvar domain_list = ${domain_list//,/ } # # Start writing up the krb5.conf file, save the existing one # if already present # writeup_krb5_conf # # Is this client going to use krb-nfs? If so then we need to at least # uncomment the krb5* sec flavors in nfssec.conf. # if [[ -z $options ]] { query "$(gettext "Do you plan on doing Kerberized nfs") ?" setvar add_nfs = "$answer" } if [[ $add_nfs == yes ]] { modify_nfssec_conf # # We also want to enable gss as we now live in a SBD world # svcadm enable svc:/network/rpc/gss:default [[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable gss service").\n" } if [[ -z $options ]] { query "$(gettext "Do you want to update /etc/pam.conf") ?" if [[ $answer == yes ]] { printf "$(gettext "Enter a list of PAM service names in the following format: service:{first|only|optional}[,..]"): " read svc_list setvar SVCs = ${svc_list//,/ } } } [[ -n $svc_list ]] && update_pam_conf # # Copy over krb5.conf master copy from filepath # if [[ -z $options || -z $filepath ]] { query "$(gettext "Do you want to copy over the master krb5.conf file") ?" if [[ $answer == yes ]] { printf "$(gettext "Enter the pathname of the file to be copied"): " read filepath } } if [[ -n $filepath && -r $filepath ]] { cp $filepath $KRB5_CONFIG if [[ $? -eq 0 ]] { printf "$(gettext "Copied %s to %s").\n" $filepath $KRB5_CONFIG } else { printf "$(gettext "Copy of %s failed, exiting").\n" $filepath >&2 error_message } } elif [[ -n $filepath ]] { printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2 error_message } doKRB5config # # Populate any service keys needed for the client in the keytab file # if [[ $no_keytab != yes ]] { setup_keytab } else { printf "\n$(gettext "Note: %s file not created, please refer to verify_ap_req_nofail in krb5.conf(4) for the implications").\n" $KRB5_KEYTAB_FILE printf "$(gettext "Client will also not be able to host services that use Kerberos").\n" } 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 ]] { 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" } # # Cleanup. # kdestroy -q 1>$TMP_FILE 2>&1 rm -f $TMP_FILE rm -rf $TMPDIR > /dev/null 2>&1 exit 0