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