Print this page
6817447 libgss and various mechs are hiding both the real minor_status and the error token
6405422 Solaris acceptors fail in AD-KDC environments when using non-"host" services (e.g. "cifs")
6824434 Unable to accept context establishment initiated by Windows 2000 clients
6787343 kclient's site lookups fail in certain network environments
6692646 kclient should output errors to stderr
6525327 kinit failed when arcfour-hmac-md5-exp was used for the principal's key
6745582 SUNWkdcu missing package dependencies after kclientv2 integration


   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 # This script is used to setup the Kerberos client by
  26 # supplying information about the Kerberos realm and kdc.
  27 #
  28 # The kerberos configuration file (/etc/krb5/krb5.conf) would
  29 # be generated and local host's keytab file setup. The script
  30 # can also optionally setup the system to do kerberized nfs and
  31 # bringover a master krb5.conf copy from a specified location.
  32 
  33 function cleanup {
  34         integer ret=$1
  35 
  36         kdestroy -q 1> $TMP_FILE 2>&1
  37         rm -r $TMPDIR > /dev/null 2>&1
  38 
  39         exit $ret
  40 }
  41 function exiting {
  42         
  43         printf "\n$(gettext "Exiting setup, nothing changed").\n\n"
  44 
  45         cleanup $1
  46 }
  47 
  48 function error_message {
  49 
  50         printf -- "---------------------------------------------------\n"
  51         printf "$(gettext "Setup FAILED").\n\n"
  52 
  53         cleanup 1
  54 }
  55 
  56 function check_bin {
  57 
  58         typeset bin=$1
  59 
  60         if [[ ! -x $bin ]]; then
  61                 printf "$(gettext "Could not access/execute %s").\n" $bin
  62                 error_message
  63         fi
  64 }
  65 
  66 function 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 
  76 function update_pam_conf {
  77         typeset PAM TPAM service
  78 
  79         PAM=/etc/pam.conf
  80 
  81         TPAM=$(mktemp -q -t kclient-pamconf.XXXXXX)


 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 
 191 function 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
 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


 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 
 863 function 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
 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 
 896 function 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"
 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 
 920 function 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 function ipAddr2num {
 943         typeset OIFS
 944         typeset -i16 num byte
 945 
 946         if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]]
 947         then
 948                 print 0
 949                 return 0
 950         fi
 951 
 952         OIFS="$IFS"
 953         IFS=.
 954         set -- $1
 955         IFS="$OIFS"
 956 
 957         num=$((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4}))
 958 
 959         print -- $num
 960 }
 961 





 962 function num2ipAddr {
 963         typeset -i16 num
 964         typeset -i10 a b c d
 965 
 966         num=$1
 967         a=$((num>>24        ))
 968         b=$((num>>16 & 16#ff))
 969         c=$((num>>8  & 16#ff))
 970         d=$((num     & 16#ff))
 971         print -- $a.$b.$c.$d
 972 }
 973 





 974 function netmask2length {
 975         typeset -i16 netmask
 976         typeset -i len
 977 
 978         netmask=$1
 979         len=32
 980         while [[ $((netmask % 2)) -eq 0 ]]
 981         do
 982                 netmask=$((netmask>>1))
 983                 len=$((len - 1))
 984         done
 985         print $len
 986 }
 987 





 988 function getSubnets {
 989         typeset -i16 addr netmask
 990         typeset -i16 classa=16\#ff000000
 991 
 992         ifconfig -a|while read line
 993         do
 994                 addr=0
 995                 netmask=0
 996                 set -- $line
 997                 [[ $1 == inet ]] || continue
 998                 while [[ $# -gt 0 ]]
 999                 do
1000                         case "$1" in
1001                                 inet) addr=$(ipAddr2num $2); shift;;
1002                                 netmask) eval netmask=16\#$2; shift;;
1003                                 *) :;
1004                         esac
1005                         shift
1006                 done
1007 
1008                 [[ $addr -eq 0 || $netmask -eq 0 ]] && continue
1009                 [[ $((addr & classa)) -eq 16\#7f000000 ]] && continue
1010 


1095                 printf "$(gettext "Warning: unable to restart %s").\n" $smbFMRI
1096         fi
1097 }
1098 
1099 function compareDomains {
1100         typeset oldDom hspn newDom=$1
1101 
1102         # If the client has been previously configured in a different
1103         # realm/domain then we need to prompt the user to see if they wish to
1104         # switch domains.
1105         klist -k 2>&1 | grep @ | read j hspn
1106         [[ -z $hspn ]] && return
1107 
1108         oldDom=${hspn#*@}
1109         if [[ $oldDom != $newDom ]]; then
1110                 printf "$(gettext "The client is currently configured in a different domain").\n"
1111                 printf "$(gettext "Currently in the '%s' domain, trying to join the '%s' domain").\n" $oldDom $newDom
1112                 query "$(gettext "Do you want the client to join a new domain") ?"
1113                 printf "\n"
1114                 if [[ $answer != yes ]]; then
1115                         printf "$(gettext "Client will not be joined to the new domain").\n"
1116                         error_message
1117                 fi
1118         fi
1119 }
1120 
1121 function getKDCDC {
1122 
1123         getKDC
1124         if [[ -n $kdc ]]; then
1125                 KDC=$kdc
1126                 dc=$kdc
1127         else
1128                 getDC
1129                 if [[ -n $dc ]]; then
1130                         KDC=$dc
1131                 else
1132                         printf "$(gettext "Could not find domain controller server for '%s'.  Exiting").\n" $realm
1133                         error_message
1134                 fi
1135         fi
1136 }
1137 
1138 function join_domain {
1139         typeset -u upcase_nodename
1140         typeset netbios_nodename fqdn
1141         
1142         container=Computers
1143         ldap_args="-o authzid= -o mech=gssapi"
1144         userAccountControlBASE=4096
1145 
1146         if [[ -z $ADMIN_PRINC ]]; then
1147                 cprinc=Administrator
1148         else
1149                 cprinc=$ADMIN_PRINC
1150         fi
1151 
1152         if ! discover_domain; then
1153                 printf "$(gettext "Can not find realm") '%s'.\n" $realm
1154                 error_message
1155         fi
1156 
1157         dom=$domain
1158         realm=$domain
1159         upcase_nodename=$hostname
1160         netbios_nodename="${upcase_nodename}\$"
1161         fqdn=$hostname.$domain
1162         upn=host/${fqdn}
1163 
1164         grep=/usr/xpg4/bin/grep
1165 
1166         object=$(mktemp -q -t kclient-computer-object.XXXXXX)
1167         if [[ -z $object ]]; then
1168                 printf "\n$(gettext "Can not create temporary file, exiting").\n
1169 " >&2
1170                 error_message
1171         fi
1172 
1173         grep=/usr/xpg4/bin/grep


1175         modify_existing=false
1176         recreate=false
1177 
1178         DomainDnsZones=$(rev_resolve DomainDnsZones.$dom.)
1179         ForestDnsZones=$(rev_resolve ForestDnsZones.$dom.)
1180 
1181         getBaseDN "$container" "$dom"
1182 
1183         if [[ -n $KDC ]]; then
1184                 dc=$KDC
1185         else
1186                 getKDCDC
1187         fi
1188 
1189         write_ads_krb5conf
1190 
1191         printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm
1192 
1193         kinit $cprinc@$realm
1194         if [[ $? -ne 0 ]]; then
1195                 printf "$(gettext "Could not authenticate %s.  Exiting").\n" $cprinc@$realm
1196                 error_message
1197         fi
1198 
1199         if getForestName
1200         then
1201                 printf "\n$(gettext "Forest name found: %s")\n\n" $forest
1202         else
1203                 printf "\n$(gettext "Forest name not found, assuming forest is the domain name").\n"
1204         fi
1205 
1206         getGC
1207         getSite
1208 
1209         if [[ -z $siteName ]]
1210         then
1211                 printf "$(gettext "Site name not found.  Local DCs/GCs will not be discovered").\n\n"
1212         else
1213                 printf "$(gettext "Looking for _local_ KDCs, DCs and global catalog servers (SRV RRs)").\n"
1214                 getKDCDC
1215                 getGC
1216 
1217                 write_ads_krb5conf
1218         fi
1219 
1220         if [[ ${#GCs} -eq 0 ]]; then
1221                 printf "$(gettext "Could not find global catalogs.  Exiting").\n"
1222                 error_message
1223         fi
1224 
1225         # Check to see if the client is transitioning between domains.
1226         compareDomains $realm
1227 
1228         # Here we check domainFunctionality to see which release:
1229         # 0, 1, 2: Windows 2000, 2003 Interim, 2003 respecitively
1230         # 3: Windows 2008
1231         level=0
1232         ldapsearch -R -T -h "$dc" $ldap_args -b "" -s base "" \
1233          domainControllerFunctionality| grep ^domainControllerFunctionality| \
1234          read j level
1235         if [[ $? -ne 0 ]]; then
1236                 printf "$(gettext "Search for domain functionality failed, exiting").\n"
1237                 error_message
1238         fi
1239         # Longhorn and above can't perform an init auth from service
1240         # keys if the realm is included in the UPN.  w2k3 and below
1241         # can't perform an init auth when the realm is excluded.
1242         [[ $level -lt 3 ]] && upn=${upn}@${realm}
1243 
1244         if ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
1245             -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1
1246         then
1247                 :
1248         else
1249                 printf "$(gettext "Search for node failed, exiting").\n"
1250                 error_message
1251         fi
1252         ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" -s sub \
1253             sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn
1254 
1255         if [[ -z $dn ]]; then
1256                 : # modify_existing is already false, which is what we want.
1257         else
1258                 printf "$(gettext "Computer account '%s' already exists in the '%s' domain").\n" $upcase_nodename $realm
1259                 query "$(gettext "Do you wish to recreate this computer account") ?"
1260                 printf "\n"
1261                 if [[ $answer == yes ]]; then
1262                         recreate=true
1263                 else
1264                         modify_existing=true
1265                 fi
1266         fi
1267 
1268         if [[ $modify_existing == false && -n $dn ]]; then
1269                 query "$(gettext "Would you like to delete any sub-object found for this computer account") ?"
1270                 if [[ $answer == yes ]]; then
1271                         printf "$(gettext "Looking to see if the machine account contains other objects")...\n"
1272                         ldapsearch -R -T -h "$dc" $ldap_args -b "$dn" -s sub "" dn | while read j sub_dn
1273                         do
1274                                 [[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue
1275                                 if $recreate; then
1276                                         printf "$(gettext "Deleting the following object: %s")\n" ${sub_dn#$dn}
1277                                         ldapdelete -h "$dc" $ldap_args "$sub_dn" > /dev/null 2>&1
1278                                         if [[ $? -ne 0 ]]; then
1279                                                 printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn}
1280                                         fi
1281                                 else
1282                                         printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn}
1283                                 fi
1284                         done
1285                 fi
1286 
1287                 if $recreate; then
1288                         ldapdelete -h "$dc" $ldap_args "$dn" > /dev/null 2>&1
1289                         if [[ $? -ne 0 ]]; then
1290                                 printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn}
1291                                 error_message
1292                         fi
1293                 elif $modify_existing; then
1294                         : # Nothing to delete
1295                 else
1296                         printf "$(gettext "A machine account already exists").\n"
1297                         error_message
1298                 fi
1299         fi
1300 
1301         if $modify_existing; then
1302                 cat > "$object" <<EOF
1303 dn: CN=$upcase_nodename,$baseDN
1304 changetype: modify
1305 replace: userPrincipalName
1306 userPrincipalName: $upn
1307 -
1308 replace: servicePrincipalName
1309 servicePrincipalName: host/${fqdn}
1310 -
1311 replace: userAccountControl
1312 userAccountControl: $((userAccountControlBASE + 32 + 2))
1313 -
1314 replace: dNSHostname
1315 dNSHostname: ${fqdn}
1316 EOF
1317 
1318                 printf "$(gettext "A machine account already exists; updating it").\n"
1319                 ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
1320                 if [[ $? -ne 0 ]]; then
1321                         printf "$(gettext "Failed to create the AD object via LDAP").\n"
1322                         error_message
1323                 fi
1324         else
1325                 cat > "$object" <<EOF
1326 dn: CN=$upcase_nodename,$baseDN
1327 objectClass: computer
1328 cn: $upcase_nodename
1329 sAMAccountName: ${netbios_nodename}
1330 userPrincipalName: $upn
1331 servicePrincipalName: host/${fqdn}
1332 userAccountControl: $((userAccountControlBASE + 32 + 2))
1333 dNSHostname: ${fqdn}
1334 EOF
1335 
1336                 printf "$(gettext "Creating the machine account in AD via LDAP").\n\n"
1337 
1338                 ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
1339                 if [[ $? -ne 0 ]]; then
1340                         printf "$(gettext "Failed to create the AD object via LDAP").\n"
1341                         error_message
1342                 fi
1343         fi
1344 
1345         # Generate a new password for the new account
1346         MAX_PASS=32
1347         i=0
1348 
1349         while :
1350         do
1351                 while ((MAX_PASS > i))
1352                 do
1353                         # 94 elements in the printable character set starting
1354                         # at decimal 33, contiguous.
1355                         dig=$((RANDOM%94+33))
1356                         c=$(printf "\\`printf %o $dig`\n")
1357                         p=$p$c
1358                         ((i+=1))
1359                 done
1360 
1361                 # Ensure that we have four character classes.
1362                 d=${p%[[:digit:]]*}
1363                 a=${p%[[:lower:]]*}
1364                 A=${p%[[:upper:]]*}
1365                 x=${p%[[:punct:]]*}
1366 
1367                 # Just compare the number of characters from what was previously
1368                 # matched.  If there is a difference then we found a match.
1369                 n=${#p}
1370                 [[ ${#d} -ne $n && ${#a} -ne $n && \
1371                    ${#A} -ne $n && ${#x} -ne $n ]] && break
1372                 i=0
1373                 p=
1374         done
1375         newpw=$p
1376 
1377         # Set the new password
1378         printf "%s" $newpw | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1
1379         if [[ $? -ne 0 ]]
1380         then
1381                 printf "$(gettext "Failed to set account password").\n"
1382                 error_message
1383         fi
1384 
1385         # Lookup the new principal's kvno:
1386         ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
1387                  -s sub cn=$upcase_nodename msDS-KeyVersionNumber| \
1388                 grep "^msDS-KeyVersionNumber"|read j kvno
1389         [[ -z $kvno ]] && kvno=1
1390 
1391         # Set supported enctypes.  This only works for Longhorn/Vista, so we
1392         # ignore errors here.
1393         userAccountControl=$((userAccountControlBASE + 524288 + 65536))
1394         set -A enctypes --
1395 
1396         # Do we have local support for AES?
1397         encrypt -l|grep ^aes|read j minkeysize maxkeysize
1398         val=
1399         if [[ $maxkeysize -eq 256 ]]; then
1400                 val=16
1401                 enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96
1402         fi
1403         if [[ $minkeysize -eq 128 ]]; then
1404                 ((val=val+8))
1405                 enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96
1406         fi
1407 
1408         # RC4 comes next (whether it's better than 1DES or not -- AD prefers it)
1409         if encrypt -l|$grep -q ^arcfour
1410         then
1411                 ((val=val+4))
1412                 enctypes[${#enctypes[@]}]=arcfour-hmac-md5
1413         else
1414                 # Use 1DES ONLY if we don't have arcfour
1415                 userAccountControl=$((userAccountControl + 2097152))
1416         fi
1417         if encrypt -l | $grep -q ^des
1418         then
1419                 ((val=val+1+2))
1420                 enctypes[${#enctypes[@]}]=des-cbc-crc
1421                 enctypes[${#enctypes[@]}]=des-cbc-md5
1422         fi
1423 
1424         if [[ ${#enctypes[@]} -eq 0 ]]
1425         then
1426                 printf "$(gettext "No enctypes are supported").\n"
1427                 printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(1M)").\n"
1428                 error_message
1429         fi
1430 
1431         # If domain crontroller is Longhorn or above then set new supported
1432         # encryption type attributes.
1433         if [[ $level -gt 2 ]]; then
1434                 cat > "$object" <<EOF
1435 dn: CN=$upcase_nodename,$baseDN
1436 changetype: modify
1437 replace: msDS-SupportedEncryptionTypes
1438 msDS-SupportedEncryptionTypes: $val
1439 EOF
1440                 ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1441                 if [[ $? -ne 0 ]]; then
1442                         printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n"
1443                 fi
1444         fi
1445 
1446         # We should probably check whether arcfour is available, and if not,
1447         # then set the 1DES only flag, but whatever, it's not likely NOT to be
1448         # available on S10/Nevada!
1449 
1450         # Reset userAccountControl
1451         #
1452         #  NORMAL_ACCOUNT (512) | DONT_EXPIRE_PASSWORD (65536) |
1453         #  TRUSTED_FOR_DELEGATION (524288)
1454         #
1455         # and possibly UseDesOnly (2097152) (see above)
1456         #
1457         cat > "$object" <<EOF
1458 dn: CN=$upcase_nodename,$baseDN
1459 changetype: modify
1460 replace: userAccountControl
1461 userAccountControl: $userAccountControl
1462 EOF
1463         ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1464         if [[ $? -ne 0 ]]; then
1465                 printf "$(gettext "ldapmodify failed to modify account attribute").\n"
1466                 error_message
1467         fi
1468 
1469         # Setup a keytab file
1470         set -A args --
1471         for enctype in "${enctypes[@]}"
1472         do
1473                 args[${#args[@]}]=-e
1474                 args[${#args[@]}]=$enctype
1475         done
1476 
1477         rm $new_keytab > /dev/null 2>&1
1478 
1479         cat > "$object" <<EOF
1480 dn: CN=$upcase_nodename,$baseDN
1481 changetype: modify
1482 add: servicePrincipalName
1483 servicePrincipalName: nfs/${fqdn}
1484 servicePrincipalName: HTTP/${fqdn}
1485 servicePrincipalName: root/${fqdn}

1486 EOF
1487         ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1488         if [[ $? -ne 0 ]]; then
1489                 printf "$(gettext "ldapmodify failed to modify account attribute").\n"
1490                 error_message
1491         fi
1492 
1493         printf "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" host/${fqdn}@${realm} > /dev/null 2>&1







1494         if [[ $? -ne 0 ]]
1495         then
1496                 printf "$(gettext "Failed to set account password").\n"
1497                 error_message
1498         fi
1499 







1500         # Could be setting ${netbios_nodename}@${realm}, but for now no one
1501         # is requesting this.
1502 
1503         print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" nfs/${fqdn}@${realm} > /dev/null 2>&1
1504         if [[ $? -ne 0 ]]
1505         then
1506                 printf "$(gettext "Failed to set account password").\n"
1507                 error_message
1508         fi
1509 
1510         print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" HTTP/${fqdn}@${realm} > /dev/null 2>&1
1511         if [[ $? -ne 0 ]]
1512         then
1513                 printf "$(gettext "Failed to set account password").\n"
1514                 error_message
1515         fi
1516 
1517         print "%s" $newpw | $KSETPW -n -v $kvno -k "$new_keytab" "${args[@]}" root/${fqdn}@${realm} > /dev/null 2>&1
1518         if [[ $? -ne 0 ]]
1519         then
1520                 printf "$(gettext "Failed to set account password").\n"
1521                 error_message
1522         fi
1523 







1524         doKRB5config
1525 
1526         addDNSRR $dom
1527 
1528         setSMB $dom $dc
1529 
1530         printf -- "\n---------------------------------------------------\n"
1531         printf "$(gettext "Setup COMPLETE").\n\n"
1532 
1533         kdestroy -q 1>$TMP_FILE 2>&1
1534         rm -f $TMP_FILE
1535         rm -rf $TMPDIR > /dev/null 2>&1
1536 
1537         exit 0
1538 }
1539 
1540 ###########################
1541 #       Main section      #
1542 ###########################
1543 #


1547 KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab
1548 RESOLV_CONF_FILE=/etc/resolv.conf
1549 
1550 KLOOKUP=/usr/lib/krb5/klookup;  check_bin $KLOOKUP
1551 KSETPW=/usr/lib/krb5/ksetpw;    check_bin $KSETPW
1552 KSMB=/usr/lib/krb5/ksmb;        check_bin $KSMB
1553 KDYNDNS=/usr/lib/krb5/kdyndns;  check_bin $KDYNDNS
1554 
1555 dns_lookup=no
1556 ask_fqdns=no
1557 adddns=no
1558 no_keytab=no
1559 checkval=""
1560 profile=""
1561 typeset -u realm
1562 typeset -l hostname KDC
1563 
1564 export TMPDIR="/var/run/kclient"
1565 
1566 mkdir $TMPDIR > /dev/null 2>&1




1567 
1568 TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX)
1569 export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX)
1570 export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) 
1571 new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) 
1572 if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]]
1573 then
1574         printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2
1575         error_message
1576 fi
1577 
1578 #
1579 # If we are interrupted, cleanup after ourselves
1580 #
1581 trap "exiting 1" HUP INT QUIT TERM
1582 
1583 if [[ -d /usr/bin ]]; then
1584         if [[ -d /usr/sbin ]]; then
1585                 PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
1586                 export PATH
1587         else
1588                 printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
1589                 exit 1
1590         fi
1591 else
1592         printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
1593         exit 1
1594 fi
1595 




   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23 # Use is subject to license terms.
  24 #
  25 # This script is used to setup the Kerberos client by
  26 # supplying information about the Kerberos realm and kdc.
  27 #
  28 # The kerberos configuration file (/etc/krb5/krb5.conf) would
  29 # be generated and local host's keytab file setup. The script
  30 # can also optionally setup the system to do kerberized nfs and
  31 # bringover a master krb5.conf copy from a specified location.
  32 
  33 function cleanup {

  34 
  35         kdestroy -q > $TMP_FILE 2>&1
  36         rm -r $TMPDIR > /dev/null 2>&1
  37 
  38         exit $1
  39 }
  40 function exiting {
  41         
  42         printf "\n$(gettext "Exiting setup, nothing changed").\n\n"
  43 
  44         cleanup $1
  45 }
  46 
  47 function error_message {
  48 
  49         printf -- "---------------------------------------------------\n" >&2
  50         printf "$(gettext "Setup FAILED").\n\n" >&2
  51 
  52         cleanup 1
  53 }
  54 
  55 function check_bin {
  56 
  57         typeset bin=$1
  58 
  59         if [[ ! -x $bin ]]; then
  60                 printf "$(gettext "Could not access/execute %s").\n" $bin >&2
  61                 error_message
  62         fi
  63 }
  64 
  65 function cannot_create {
  66         typeset filename="$1"
  67         typeset stat="$2"
  68 
  69         if [[ $stat -ne 0 ]]; then
  70                 printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2
  71                 error_message
  72         fi
  73 }
  74 
  75 function update_pam_conf {
  76         typeset PAM TPAM service
  77 
  78         PAM=/etc/pam.conf
  79 
  80         TPAM=$(mktemp -q -t kclient-pamconf.XXXXXX)


 177         kadmin -c $KRB5CCNAME -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1
 178         egrep -s "$(gettext "added to keytab WRFILE:$KRB5_KEYTAB_FILE.")" $TMP_FILE
 179         if [[ $? -ne 0 ]]; then
 180                 cat $TMP_FILE;
 181                 printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ >&2
 182                 error_message
 183         else
 184                 printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ
 185         fi
 186 
 187         done
 188 }
 189 
 190 function writeup_krb5_conf {
 191         typeset dh
 192 
 193         printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE
 194 
 195         exec 3>$KRB5_CONFIG
 196         if [[ $? -ne 0 ]]; then
 197                 printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2
 198                 error_message
 199         fi
 200 
 201         printf "[libdefaults]\n" 1>&3
 202         if [[ $no_keytab == yes ]]; then
 203                 printf "\tverify_ap_req_nofail = false\n" 1>&3
 204         fi
 205         if [[ $dns_lookup == yes ]]; then
 206             printf "\t$dnsarg = on\n" 1>&3
 207             if [[ $dnsarg == dns_lookup_kdc ]]; then
 208                 printf "\tdefault_realm = $realm\n" 1>&3
 209                 printf "\n[domain_realm]\n" 1>&3
 210                 if [[ -n $fkdc_list ]]; then
 211                         for kdc in $fkdc_list; do
 212                                 printf "\t$kdc = $realm\n" 1>&3
 213                         done
 214                 fi
 215                 printf "\t$FKDC = $realm\n" 1>&3
 216                 printf "\t$client_machine = $realm\n" 1>&3
 217                 if [[ -z $short_fqdn ]]; then


 847                 set -A DCs -- $(getSRVs _ldap._tcp.$siteName._sites.dc._msdcs.$dom.)
 848                 dc=${DCs[0]}
 849                 [[ -n $dc ]] && return
 850         fi
 851 
 852         # No site name
 853         set -A DCs -- $(getSRVs _ldap._tcp.dc._msdcs.$dom.)
 854         dc=${DCs[0]}
 855         [[ -n $dc ]] && return
 856 
 857         # Default
 858         set -A DCs -- $DomainDnsZones 389
 859         dc=$DomainDnsZones
 860 }
 861 
 862 function write_ads_krb5conf {
 863         printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE
 864 
 865         exec 3>$KRB5_CONFIG
 866         if [[ $? -ne 0 ]]; then
 867                 printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2
 868                 error_message
 869         fi
 870 
 871         printf "[libdefaults]\n" 1>&3
 872         printf "\tdefault_realm = $realm\n" 1>&3
 873         printf "\n[realms]\n" 1>&3
 874         printf "\t$realm = {\n" 1>&3
 875         for i in ${KDCs[@]}
 876         do
 877                 [[ $i == +([0-9]) ]] && continue
 878                 printf "\t\tkdc = $i\n" 1>&3
 879         done
 880         # Defining the same as admin_server.  This would cause auth failures
 881         # if this was different.
 882         printf "\n\t\tkpasswd_server = $KDC\n" 1>&3
 883         printf "\n\t\tadmin_server = $KDC\n" 1>&3
 884         printf "\t\tkpasswd_protocol = SET_CHANGE\n\t}\n" 1>&3
 885         printf "\n[domain_realm]\n" 1>&3
 886         printf "\t.$dom = $realm\n\n" 1>&3
 887         printf "[logging]\n" 1>&3
 888         printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3
 889         printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3
 890         printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3
 891         printf "[appdefaults]\n" 1>&3
 892         printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n\t}\n" 1>&3
 893 }
 894 
 895 function getForestName {
 896         ldapsearch -R -T -h $dc $ldap_args \
 897             -b "" -s base "" schemaNamingContext| \
 898                 grep ^schemaNamingContext|read j schemaNamingContext
 899 
 900         if [[ $? -ne 0 ]]; then
 901                 printf "$(gettext "Can't find forest").\n" >&2
 902                 error_message
 903         fi
 904         schemaNamingContext=${schemaNamingContext#CN=Schema,CN=Configuration,}
 905 
 906         [[ -z $schemaNamingContext ]] && return 1
 907 
 908         forest=
 909         while [[ -n $schemaNamingContext ]]
 910         do
 911                 schemaNamingContext=${schemaNamingContext#DC=}
 912                 forest=${forest}.${schemaNamingContext%%,*}
 913                 [[ "$schemaNamingContext" = *,* ]] || break
 914                 schemaNamingContext=${schemaNamingContext#*,}
 915         done
 916         forest=${forest#.}
 917 }
 918 
 919 function getGC {
 920         typeset j
 921 
 922         [[ -n $gc ]] && return 0
 923 
 924         if [[ -n $siteName ]]
 925         then
 926                 set -A GCs -- $(getSRVs _ldap._tcp.$siteName._sites.gc._msdcs.$forest.)
 927                 gc=${GCs[0]}
 928                 [[ -n $gc ]] && return
 929         fi
 930 
 931         # No site name
 932         set -A GCs -- $(getSRVs _ldap._tcp.gc._msdcs.$forest.)
 933         gc=${GCs[0]}
 934         [[ -n $gc ]] && return
 935 
 936         # Default
 937         set -A GCs -- $ForestDnsZones 3268
 938         gc=$ForestDnsZones
 939 }
 940 
 941 #
 942 # The local variables used to calculate the IP address are of type unsigned
 943 # integer (-ui), as this is required to restrict the integer to 32b.
 944 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
 945 #
 946 function ipAddr2num {
 947         typeset OIFS
 948         typeset -ui16 num
 949 
 950         if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]]
 951         then
 952                 print 0
 953                 return 0
 954         fi
 955 
 956         OIFS="$IFS"
 957         IFS=.
 958         set -- $1
 959         IFS="$OIFS"
 960 
 961         num=$((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4}))
 962 
 963         print -- $num
 964 }
 965 
 966 #
 967 # The local variables used to calculate the IP address are of type unsigned
 968 # integer (-ui), as this is required to restrict the integer to 32b.
 969 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
 970 #
 971 function num2ipAddr {
 972         typeset -ui16 num
 973         typeset -ui10 a b c d
 974 
 975         num=$1
 976         a=$((num>>24        ))
 977         b=$((num>>16 & 16#ff))
 978         c=$((num>>8  & 16#ff))
 979         d=$((num     & 16#ff))
 980         print -- $a.$b.$c.$d
 981 }
 982 
 983 #
 984 # The local variables used to calculate the IP address are of type unsigned
 985 # integer (-ui), as this is required to restrict the integer to 32b.
 986 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
 987 #
 988 function netmask2length {
 989         typeset -ui16 netmask
 990         typeset -i len
 991 
 992         netmask=$1
 993         len=32
 994         while [[ $((netmask % 2)) -eq 0 ]]
 995         do
 996                 netmask=$((netmask>>1))
 997                 len=$((len - 1))
 998         done
 999         print $len
1000 }
1001 
1002 #
1003 # The local variables used to calculate the IP address are of type unsigned
1004 # integer (-ui), as this is required to restrict the integer to 32b.
1005 # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b.
1006 #
1007 function getSubnets {
1008         typeset -ui16 addr netmask
1009         typeset -ui16 classa=16\#ff000000
1010 
1011         ifconfig -a|while read line
1012         do
1013                 addr=0
1014                 netmask=0
1015                 set -- $line
1016                 [[ $1 == inet ]] || continue
1017                 while [[ $# -gt 0 ]]
1018                 do
1019                         case "$1" in
1020                                 inet) addr=$(ipAddr2num $2); shift;;
1021                                 netmask) eval netmask=16\#$2; shift;;
1022                                 *) :;
1023                         esac
1024                         shift
1025                 done
1026 
1027                 [[ $addr -eq 0 || $netmask -eq 0 ]] && continue
1028                 [[ $((addr & classa)) -eq 16\#7f000000 ]] && continue
1029 


1114                 printf "$(gettext "Warning: unable to restart %s").\n" $smbFMRI
1115         fi
1116 }
1117 
1118 function compareDomains {
1119         typeset oldDom hspn newDom=$1
1120 
1121         # If the client has been previously configured in a different
1122         # realm/domain then we need to prompt the user to see if they wish to
1123         # switch domains.
1124         klist -k 2>&1 | grep @ | read j hspn
1125         [[ -z $hspn ]] && return
1126 
1127         oldDom=${hspn#*@}
1128         if [[ $oldDom != $newDom ]]; then
1129                 printf "$(gettext "The client is currently configured in a different domain").\n"
1130                 printf "$(gettext "Currently in the '%s' domain, trying to join the '%s' domain").\n" $oldDom $newDom
1131                 query "$(gettext "Do you want the client to join a new domain") ?"
1132                 printf "\n"
1133                 if [[ $answer != yes ]]; then
1134                         printf "$(gettext "Client will not be joined to the new domain").\n" >&2
1135                         error_message
1136                 fi
1137         fi
1138 }
1139 
1140 function getKDCDC {
1141 
1142         getKDC
1143         if [[ -n $kdc ]]; then
1144                 KDC=$kdc
1145                 dc=$kdc
1146         else
1147                 getDC
1148                 if [[ -n $dc ]]; then
1149                         KDC=$dc
1150                 else
1151                         printf "$(gettext "Could not find domain controller server for '%s'.  Exiting").\n" $realm >&2
1152                         error_message
1153                 fi
1154         fi
1155 }
1156 
1157 function join_domain {
1158         typeset -u upcase_nodename
1159         typeset netbios_nodename fqdn
1160         
1161         container=Computers
1162         ldap_args="-o authzid= -o mech=gssapi"
1163         userAccountControlBASE=4096
1164 
1165         if [[ -z $ADMIN_PRINC ]]; then
1166                 cprinc=Administrator
1167         else
1168                 cprinc=$ADMIN_PRINC
1169         fi
1170 
1171         if ! discover_domain; then
1172                 printf "$(gettext "Can not find realm") '%s'.\n" $realm >&2
1173                 error_message
1174         fi
1175 
1176         dom=$domain
1177         realm=$domain
1178         upcase_nodename=$hostname
1179         netbios_nodename="${upcase_nodename}\$"
1180         fqdn=$hostname.$domain
1181         upn=host/${fqdn}
1182 
1183         grep=/usr/xpg4/bin/grep
1184 
1185         object=$(mktemp -q -t kclient-computer-object.XXXXXX)
1186         if [[ -z $object ]]; then
1187                 printf "\n$(gettext "Can not create temporary file, exiting").\n
1188 " >&2
1189                 error_message
1190         fi
1191 
1192         grep=/usr/xpg4/bin/grep


1194         modify_existing=false
1195         recreate=false
1196 
1197         DomainDnsZones=$(rev_resolve DomainDnsZones.$dom.)
1198         ForestDnsZones=$(rev_resolve ForestDnsZones.$dom.)
1199 
1200         getBaseDN "$container" "$dom"
1201 
1202         if [[ -n $KDC ]]; then
1203                 dc=$KDC
1204         else
1205                 getKDCDC
1206         fi
1207 
1208         write_ads_krb5conf
1209 
1210         printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm
1211 
1212         kinit $cprinc@$realm
1213         if [[ $? -ne 0 ]]; then
1214                 printf "$(gettext "Could not authenticate %s.  Exiting").\n" $cprinc@$realm >&2
1215                 error_message
1216         fi
1217 
1218         if getForestName
1219         then
1220                 printf "\n$(gettext "Forest name found: %s")\n\n" $forest
1221         else
1222                 printf "\n$(gettext "Forest name not found, assuming forest is the domain name").\n"
1223         fi
1224 
1225         getGC
1226         getSite
1227 
1228         if [[ -z $siteName ]]
1229         then
1230                 printf "$(gettext "Site name not found.  Local DCs/GCs will not be discovered").\n\n"
1231         else
1232                 printf "$(gettext "Looking for _local_ KDCs, DCs and global catalog servers (SRV RRs)").\n"
1233                 getKDCDC
1234                 getGC
1235 
1236                 write_ads_krb5conf
1237         fi
1238 
1239         if [[ ${#GCs} -eq 0 ]]; then
1240                 printf "$(gettext "Could not find global catalogs.  Exiting").\n" >&2
1241                 error_message
1242         fi
1243 
1244         # Check to see if the client is transitioning between domains.
1245         compareDomains $realm
1246 
1247         # Here we check domainFunctionality to see which release:
1248         # 0, 1, 2: Windows 2000, 2003 Interim, 2003 respecitively
1249         # 3: Windows 2008
1250         level=0
1251         ldapsearch -R -T -h "$dc" $ldap_args -b "" -s base "" \
1252          domainControllerFunctionality| grep ^domainControllerFunctionality| \
1253          read j level
1254         if [[ $? -ne 0 ]]; then
1255                 printf "$(gettext "Search for domain functionality failed, exiting").\n" >&2
1256                 error_message
1257         fi
1258         # Longhorn and above can't perform an init auth from service
1259         # keys if the realm is included in the UPN.  w2k3 and below
1260         # can't perform an init auth when the realm is excluded.
1261         [[ $level -lt 3 ]] && upn=${upn}@${realm}
1262 
1263         if ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
1264             -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1
1265         then
1266                 :
1267         else
1268                 printf "$(gettext "Search for node failed, exiting").\n" >&2
1269                 error_message
1270         fi
1271         ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" -s sub \
1272             sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn
1273 
1274         if [[ -z $dn ]]; then
1275                 : # modify_existing is already false, which is what we want.
1276         else
1277                 printf "$(gettext "Computer account '%s' already exists in the '%s' domain").\n" $upcase_nodename $realm
1278                 query "$(gettext "Do you wish to recreate this computer account") ?"
1279                 printf "\n"
1280                 if [[ $answer == yes ]]; then
1281                         recreate=true
1282                 else
1283                         modify_existing=true
1284                 fi
1285         fi
1286 
1287         if [[ $modify_existing == false && -n $dn ]]; then
1288                 query "$(gettext "Would you like to delete any sub-object found for this computer account") ?"
1289                 if [[ $answer == yes ]]; then
1290                         printf "$(gettext "Looking to see if the machine account contains other objects")...\n"
1291                         ldapsearch -R -T -h "$dc" $ldap_args -b "$dn" -s sub "" dn | while read j sub_dn
1292                         do
1293                                 [[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue
1294                                 if $recreate; then
1295                                         printf "$(gettext "Deleting the following object: %s")\n" ${sub_dn#$dn}
1296                                         ldapdelete -h "$dc" $ldap_args "$sub_dn" > /dev/null 2>&1
1297                                         if [[ $? -ne 0 ]]; then
1298                                                 printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn}
1299                                         fi
1300                                 else
1301                                         printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn}
1302                                 fi
1303                         done
1304                 fi
1305 
1306                 if $recreate; then
1307                         ldapdelete -h "$dc" $ldap_args "$dn" > /dev/null 2>&1
1308                         if [[ $? -ne 0 ]]; then
1309                                 printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} >&2
1310                                 error_message
1311                         fi
1312                 elif $modify_existing; then
1313                         : # Nothing to delete
1314                 else
1315                         printf "$(gettext "A machine account already exists").\n" >&2
1316                         error_message
1317                 fi
1318         fi
1319 
1320         if $modify_existing; then
1321                 cat > "$object" <<EOF
1322 dn: CN=$upcase_nodename,$baseDN
1323 changetype: modify
1324 replace: userPrincipalName
1325 userPrincipalName: $upn
1326 -
1327 replace: servicePrincipalName
1328 servicePrincipalName: host/${fqdn}
1329 -
1330 replace: userAccountControl
1331 userAccountControl: $((userAccountControlBASE + 32 + 2))
1332 -
1333 replace: dNSHostname
1334 dNSHostname: ${fqdn}
1335 EOF
1336 
1337                 printf "$(gettext "A machine account already exists; updating it").\n"
1338                 ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
1339                 if [[ $? -ne 0 ]]; then
1340                         printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2
1341                         error_message
1342                 fi
1343         else
1344                 cat > "$object" <<EOF
1345 dn: CN=$upcase_nodename,$baseDN
1346 objectClass: computer
1347 cn: $upcase_nodename
1348 sAMAccountName: ${netbios_nodename}
1349 userPrincipalName: $upn
1350 servicePrincipalName: host/${fqdn}
1351 userAccountControl: $((userAccountControlBASE + 32 + 2))
1352 dNSHostname: ${fqdn}
1353 EOF
1354 
1355                 printf "$(gettext "Creating the machine account in AD via LDAP").\n\n"
1356 
1357                 ldapadd -h "$dc" $ldap_args -f "$object" > /dev/null 2>&1
1358                 if [[ $? -ne 0 ]]; then
1359                         printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2
1360                         error_message
1361                 fi
1362         fi
1363 
1364         # Generate a new password for the new account
1365         MAX_PASS=32
1366         i=0
1367 
1368         while :
1369         do
1370                 while ((MAX_PASS > i))
1371                 do
1372                         # 94 elements in the printable character set starting
1373                         # at decimal 33, contiguous.
1374                         dig=$((RANDOM%94+33))
1375                         c=$(printf "\\`printf %o $dig`\n")
1376                         p=$p$c
1377                         ((i+=1))
1378                 done
1379 
1380                 # Ensure that we have four character classes.
1381                 d=${p%[[:digit:]]*}
1382                 a=${p%[[:lower:]]*}
1383                 A=${p%[[:upper:]]*}
1384                 x=${p%[[:punct:]]*}
1385 
1386                 # Just compare the number of characters from what was previously
1387                 # matched.  If there is a difference then we found a match.
1388                 n=${#p}
1389                 [[ ${#d} -ne $n && ${#a} -ne $n && \
1390                    ${#A} -ne $n && ${#x} -ne $n ]] && break
1391                 i=0
1392                 p=
1393         done
1394         newpw=$p
1395 
1396         # Set the new password
1397         printf "%s" $newpw | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1
1398         if [[ $? -ne 0 ]]
1399         then
1400                 printf "$(gettext "Failed to set account password").\n" >&2
1401                 error_message
1402         fi
1403 
1404         # Lookup the new principal's kvno:
1405         ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \
1406                  -s sub cn=$upcase_nodename msDS-KeyVersionNumber| \
1407                 grep "^msDS-KeyVersionNumber"|read j kvno
1408         [[ -z $kvno ]] && kvno=1
1409 
1410         # Set supported enctypes.  This only works for Longhorn/Vista, so we
1411         # ignore errors here.
1412         userAccountControl=$((userAccountControlBASE + 524288 + 65536))
1413         set -A enctypes --
1414 
1415         # Do we have local support for AES?
1416         encrypt -l|grep ^aes|read j minkeysize maxkeysize
1417         val=
1418         if [[ $maxkeysize -eq 256 ]]; then
1419                 val=16
1420                 enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96
1421         fi
1422         if [[ $minkeysize -eq 128 ]]; then
1423                 ((val=val+8))
1424                 enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96
1425         fi
1426 
1427         # RC4 comes next (whether it's better than 1DES or not -- AD prefers it)
1428         if encrypt -l|$grep -q ^arcfour
1429         then
1430                 ((val=val+4))
1431                 enctypes[${#enctypes[@]}]=arcfour-hmac-md5
1432         else
1433                 # Use 1DES ONLY if we don't have arcfour
1434                 userAccountControl=$((userAccountControl + 2097152))
1435         fi
1436         if encrypt -l | $grep -q ^des
1437         then
1438                 ((val=val+2))

1439                 enctypes[${#enctypes[@]}]=des-cbc-md5
1440         fi
1441 
1442         if [[ ${#enctypes[@]} -eq 0 ]]
1443         then
1444                 printf "$(gettext "No enctypes are supported").\n"
1445                 printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(1M)").\n" >&2
1446                 error_message
1447         fi
1448 
1449         # If domain crontroller is Longhorn or above then set new supported
1450         # encryption type attributes.
1451         if [[ $level -gt 2 ]]; then
1452                 cat > "$object" <<EOF
1453 dn: CN=$upcase_nodename,$baseDN
1454 changetype: modify
1455 replace: msDS-SupportedEncryptionTypes
1456 msDS-SupportedEncryptionTypes: $val
1457 EOF
1458                 ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1459                 if [[ $? -ne 0 ]]; then
1460                         printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n"
1461                 fi
1462         fi
1463 
1464         # We should probably check whether arcfour is available, and if not,
1465         # then set the 1DES only flag, but whatever, it's not likely NOT to be
1466         # available on S10/Nevada!
1467 
1468         # Reset userAccountControl
1469         #
1470         #  NORMAL_ACCOUNT (512) | DONT_EXPIRE_PASSWORD (65536) |
1471         #  TRUSTED_FOR_DELEGATION (524288)
1472         #
1473         # and possibly UseDesOnly (2097152) (see above)
1474         #
1475         cat > "$object" <<EOF
1476 dn: CN=$upcase_nodename,$baseDN
1477 changetype: modify
1478 replace: userAccountControl
1479 userAccountControl: $userAccountControl
1480 EOF
1481         ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1482         if [[ $? -ne 0 ]]; then
1483                 printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
1484                 error_message
1485         fi
1486 
1487         # Setup a keytab file
1488         set -A args --
1489         for enctype in "${enctypes[@]}"
1490         do
1491                 args[${#args[@]}]=-e
1492                 args[${#args[@]}]=$enctype
1493         done
1494 
1495         rm $new_keytab > /dev/null 2>&1
1496 
1497         cat > "$object" <<EOF
1498 dn: CN=$upcase_nodename,$baseDN
1499 changetype: modify
1500 add: servicePrincipalName
1501 servicePrincipalName: nfs/${fqdn}
1502 servicePrincipalName: HTTP/${fqdn}
1503 servicePrincipalName: root/${fqdn}
1504 servicePrincipalName: cifs/${fqdn}
1505 EOF
1506         ldapmodify -h "$dc" $ldap_args -f "$object" >/dev/null 2>&1
1507         if [[ $? -ne 0 ]]; then
1508                 printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2
1509                 error_message
1510         fi
1511 
1512         #
1513         # In Windows, unlike MIT based implementations we salt the keys with
1514         # the UPN, which is based on the host/fqdn@realm elements, not with the
1515         # individual SPN strings.
1516         #
1517         salt=host/${fqdn}@${realm}
1518 
1519         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" host/${fqdn}@${realm} > /dev/null 2>&1
1520         if [[ $? -ne 0 ]]
1521         then
1522                 printf "$(gettext "Failed to set account password").\n" >&2
1523                 error_message
1524         fi
1525 
1526         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" HOST/${fqdn}@${realm} > /dev/null 2>&1
1527         if [[ $? -ne 0 ]]
1528         then
1529                 printf "$(gettext "Failed to set account password").\n" >&2
1530                 error_message
1531         fi
1532 
1533         # Could be setting ${netbios_nodename}@${realm}, but for now no one
1534         # is requesting this.
1535 
1536         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" nfs/${fqdn}@${realm} > /dev/null 2>&1
1537         if [[ $? -ne 0 ]]
1538         then
1539                 printf "$(gettext "Failed to set account password").\n" >&2
1540                 error_message
1541         fi
1542 
1543         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" HTTP/${fqdn}@${realm} > /dev/null 2>&1
1544         if [[ $? -ne 0 ]]
1545         then
1546                 printf "$(gettext "Failed to set account password").\n" >&2
1547                 error_message
1548         fi
1549 
1550         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" root/${fqdn}@${realm} > /dev/null 2>&1
1551         if [[ $? -ne 0 ]]
1552         then
1553                 printf "$(gettext "Failed to set account password").\n" >&2
1554                 error_message
1555         fi
1556 
1557         printf "%s" $newpw | $KSETPW -n -s $salt -v $kvno -k "$new_keytab" "${args[@]}" cifs/${fqdn}@${realm} > /dev/null 2>&1
1558         if [[ $? -ne 0 ]]
1559         then
1560                 printf "$(gettext "Failed to set account password").\n" >&2
1561                 error_message
1562         fi
1563 
1564         doKRB5config
1565 
1566         addDNSRR $dom
1567 
1568         setSMB $dom $dc
1569 
1570         printf -- "\n---------------------------------------------------\n"
1571         printf "$(gettext "Setup COMPLETE").\n\n"
1572 
1573         kdestroy -q 1>$TMP_FILE 2>&1
1574         rm -f $TMP_FILE
1575         rm -rf $TMPDIR > /dev/null 2>&1
1576 
1577         exit 0
1578 }
1579 
1580 ###########################
1581 #       Main section      #
1582 ###########################
1583 #


1587 KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab
1588 RESOLV_CONF_FILE=/etc/resolv.conf
1589 
1590 KLOOKUP=/usr/lib/krb5/klookup;  check_bin $KLOOKUP
1591 KSETPW=/usr/lib/krb5/ksetpw;    check_bin $KSETPW
1592 KSMB=/usr/lib/krb5/ksmb;        check_bin $KSMB
1593 KDYNDNS=/usr/lib/krb5/kdyndns;  check_bin $KDYNDNS
1594 
1595 dns_lookup=no
1596 ask_fqdns=no
1597 adddns=no
1598 no_keytab=no
1599 checkval=""
1600 profile=""
1601 typeset -u realm
1602 typeset -l hostname KDC
1603 
1604 export TMPDIR="/var/run/kclient"
1605 
1606 mkdir $TMPDIR > /dev/null 2>&1
1607 if [[ $? -ne 0 ]]; then
1608         printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2
1609         exit 1
1610 fi
1611 
1612 TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX)
1613 export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX)
1614 export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) 
1615 new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) 
1616 if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]]
1617 then
1618         printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2
1619         exit 1
1620 fi
1621 
1622 #
1623 # If we are interrupted, cleanup after ourselves
1624 #
1625 trap "exiting 1" HUP INT QUIT TERM
1626 
1627 if [[ -d /usr/bin ]]; then
1628         if [[ -d /usr/sbin ]]; then
1629                 PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
1630                 export PATH
1631         else
1632                 printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2
1633                 exit 1
1634         fi
1635 else
1636         printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2
1637         exit 1
1638 fi
1639