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 33function cleanup { 34 35 kdestroy -q > $TMP_FILE 2>&1 36 rm -r $TMPDIR > /dev/null 2>&1 37 38 exit $1 39} 40function exiting { 41 42 printf "\n$(gettext "Exiting setup, nothing changed").\n\n" 43 44 cleanup $1 45} 46 47function error_message { 48 49 printf -- "---------------------------------------------------\n" >&2 50 printf "$(gettext "Setup FAILED").\n\n" >&2 51 52 cleanup 1 53} 54 55function 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 65function 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 75function 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 107function 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 124function 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 190function 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 299function 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 312function 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 332function query { 333 yesno "$*" 334 335 if [[ $answer == no ]]; then 336 printf "\t$(gettext "No action performed").\n" 337 fi 338} 339 340 341function 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 430function 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 448break 449 fi 450} 451 452function 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 467function 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 482function 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 511function 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 523function 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 563function 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 636function 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 659function 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 663tiple 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 680function 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 701function 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 711function 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 723function 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 747function 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 760function 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 781function 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 791function 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 808function 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 819function 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 842function 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 862function 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 895function 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 919function 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# 946function 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# 971function 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# 988function 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# 1007function 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 1034function 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 1059function 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 1072function 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 1101function 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 1118function 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 1140function 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 1157function 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 1322dn: CN=$upcase_nodename,$baseDN 1323changetype: modify 1324replace: userPrincipalName 1325userPrincipalName: $upn 1326- 1327replace: servicePrincipalName 1328servicePrincipalName: host/${fqdn} 1329- 1330replace: userAccountControl 1331userAccountControl: $((userAccountControlBASE + 32 + 2)) 1332- 1333replace: dNSHostname 1334dNSHostname: ${fqdn} 1335EOF 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 1345dn: CN=$upcase_nodename,$baseDN 1346objectClass: computer 1347cn: $upcase_nodename 1348sAMAccountName: ${netbios_nodename} 1349userPrincipalName: $upn 1350servicePrincipalName: host/${fqdn} 1351userAccountControl: $((userAccountControlBASE + 32 + 2)) 1352dNSHostname: ${fqdn} 1353EOF 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 1453dn: CN=$upcase_nodename,$baseDN 1454changetype: modify 1455replace: msDS-SupportedEncryptionTypes 1456msDS-SupportedEncryptionTypes: $val 1457EOF 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 1476dn: CN=$upcase_nodename,$baseDN 1477changetype: modify 1478replace: userAccountControl 1479userAccountControl: $userAccountControl 1480EOF 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 1498dn: CN=$upcase_nodename,$baseDN 1499changetype: modify 1500add: servicePrincipalName 1501servicePrincipalName: nfs/${fqdn} 1502servicePrincipalName: HTTP/${fqdn} 1503servicePrincipalName: root/${fqdn} 1504servicePrincipalName: cifs/${fqdn} 1505EOF 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# 1586KRB5_CONFIG_FILE=/etc/krb5/krb5.conf 1587KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab 1588RESOLV_CONF_FILE=/etc/resolv.conf 1589 1590KLOOKUP=/usr/lib/krb5/klookup; check_bin $KLOOKUP 1591KSETPW=/usr/lib/krb5/ksetpw; check_bin $KSETPW 1592KSMB=/usr/lib/krb5/ksmb; check_bin $KSMB 1593KDYNDNS=/usr/lib/krb5/kdyndns; check_bin $KDYNDNS 1594 1595dns_lookup=no 1596ask_fqdns=no 1597adddns=no 1598no_keytab=no 1599checkval="" 1600profile="" 1601typeset -u realm 1602typeset -l hostname KDC 1603 1604export TMPDIR="/var/run/kclient" 1605 1606mkdir $TMPDIR > /dev/null 2>&1 1607if [[ $? -ne 0 ]]; then 1608 printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2 1609 exit 1 1610fi 1611 1612TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX) 1613export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX) 1614export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) 1615new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) 1616if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]] 1617then 1618 printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2 1619 exit 1 1620fi 1621 1622# 1623# If we are interrupted, cleanup after ourselves 1624# 1625trap "exiting 1" HUP INT QUIT TERM 1626 1627if [[ -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 1635else 1636 printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2 1637 exit 1 1638fi 1639 1640printf "\n$(gettext "Starting client setup")\n\n" 1641printf -- "---------------------------------------------------\n" 1642 1643# 1644# Check for uid 0, disallow otherwise 1645# 1646id 1>$TMP_FILE 2>&1 1647if [[ $? -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 1655else 1656 cat $TMP_FILE; 1657 printf "\n$(gettext "uid check failed, exiting").\n" >&2 1658 error_message 1659fi 1660 1661uname=$(uname -n) 1662hostname=${uname%%.*} 1663 1664# 1665# Process the command-line arguments (if any) 1666# 1667OPTIND=1 1668while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T: OPTIONS 1669do 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 1732done 1733 1734#correct argument count after options 1735shift `expr $OPTIND - 1` 1736 1737if [[ -z $options ]]; then 1738 : 1739else 1740 if [[ $# -ne 0 ]]; then 1741 usage 1742 fi 1743fi 1744 1745# 1746# Check to see if we will be a client of a MIT, Heimdal, Shishi, etc. 1747# 1748if [[ -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 1769fi 1770 1771[[ $msad == yes ]] && join_domain 1772 1773# 1774# Check for /etc/resolv.conf 1775# 1776if [[ -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 "\.") 1797else 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 1804fi 1805 1806check_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 1810if [[ -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 1819else 1820 [[ -z $dnsarg ]] && dnsarg=none 1821 set_dns_value $dnsarg 1822fi 1823 1824if [[ -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 1831fi 1832 1833if [[ -z $realm ]]; then 1834 printf "$(gettext "Enter the Kerberos realm"): " 1835 read realm 1836 checkval="REALM"; check_value $realm 1837fi 1838if [[ -z $KDC ]]; then 1839 printf "$(gettext "Specify the master KDC hostname for the above realm"): " 1840 read KDC 1841 checkval="KDC"; check_value $KDC 1842fi 1843 1844FKDC=`$KLOOKUP $KDC` 1845 1846# 1847# Ping to see if the kdc is alive ! 1848# 1849ping_check $FKDC "KDC" 1850 1851if [[ -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 1857fi 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# 1864if [[ -z $options ]]; then 1865 query "$(gettext "Will this client need service keys") ?" 1866 if [[ $answer == no ]]; then 1867 no_keytab=yes 1868 fi 1869fi 1870 1871# 1872# Check to see if we are configuring the client to use a logical host name 1873# of a cluster environment 1874# 1875if [[ -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 1883fi 1884 1885if [[ -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 1890to map to the default realm"): " 1891 read domain_list 1892 fi 1893fi 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# 1900writeup_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# 1906if [[ -z $options ]]; then 1907 query "$(gettext "Do you plan on doing Kerberized nfs") ?" 1908 add_nfs=$answer 1909fi 1910 1911if [[ $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" 1919fi 1920 1921if [[ -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 1928fi 1929[[ -n $svc_list ]] && update_pam_conf 1930 1931# 1932# Copy over krb5.conf master copy from filepath 1933# 1934if [[ -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 1940fi 1941 1942if [[ -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 1950elif [[ -n $filepath ]]; then 1951 printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2 1952 error_message 1953fi 1954 1955doKRB5config 1956 1957# 1958# Populate any service keys needed for the client in the keytab file 1959# 1960if [[ $no_keytab != yes ]]; then 1961 setup_keytab 1962else 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" 1965fi 1966 1967printf -- "\n---------------------------------------------------\n" 1968printf "$(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# 1974if [[ -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" 1976fi 1977 1978# 1979# Cleanup. 1980# 1981kdestroy -q 1>$TMP_FILE 2>&1 1982rm -f $TMP_FILE 1983rm -rf $TMPDIR > /dev/null 2>&1 1984exit 0 1985