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
|