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