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