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