s4:upgradeprovision Rework script, and reset machine account pw
authorAndrew Bartlett <abartlet@samba.org>
Thu, 26 Nov 2009 04:32:49 +0000 (15:32 +1100)
committerAndrew Tridgell <tridge@samba.org>
Fri, 27 Nov 2009 05:05:04 +0000 (16:05 +1100)
The rework corrects some duplication and errors in the original
script, found when preparing an automated test of the script.

The code to reset the machine account password avoids issues with AES
keys and salting, which may not otherwise be solved by the upgrade.

Andrew Bartlett

source4/scripting/bin/upgradeprovision

index 74d9829376acbcf48a24b51644b48d54620ebd1b..7dd9a95b1e6c641b76081b53f792a8e805d381a8 100755 (executable)
@@ -45,9 +45,11 @@ import samba.getopt as options
 from samba.samdb import SamDB
 from samba import param
 from samba.provision import  ProvisionNames,provision_paths_from_lp,find_setup_dir,FILL_FULL,provision
+from samba.provisionexceptions import ProvisioningError
 from samba.schema import get_dnsyntax_attributes, get_linked_attributes, Schema
 from samba.dcerpc import misc, security
 from samba.ndr import ndr_pack, ndr_unpack
+from samba.dcerpc.misc import SEC_CHAN_BDC
 
 replace=2^ldb.FLAG_MOD_REPLACE
 add=2^ldb.FLAG_MOD_ADD
@@ -171,8 +173,7 @@ def guess_names_from_current_provision(credentials,session_info,paths):
        # NT domain, kerberos realm, root dn, domain dn, domain dns name
        names.domain = string.upper(lp.get("workgroup"))
        names.realm = lp.get("realm")
-       rootdn = "DC=" + names.realm.replace(".",",DC=")
-       names.domaindn = rootdn
+       basedn = "DC=" + names.realm.replace(".",",DC=")
        names.dnsdomain = names.realm
        names.realm = string.upper(names.realm)
        # netbiosname
@@ -188,16 +189,17 @@ def guess_names_from_current_provision(credentials,session_info,paths):
                    credentials=credentials, lp=lp, options=["modules:samba_dsdb"])
 
        # That's a bit simplistic but it's ok as long as we have only 3 partitions
-       attrs2 = ["schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
+       attrs2 = ["defaultNamingContext", "schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
        res2 = samdb.search(expression="(objectClass=*)",base="", scope=SCOPE_BASE, attrs=attrs2)
 
        names.configdn = res2[0]["configurationNamingContext"]
        configdn = str(names.configdn)
        names.schemadn = res2[0]["schemaNamingContext"]
-       if not (rootdn == str(res2[0]["rootDomainNamingContext"])):
-               message(ERROR, "rootdn in sam.ldb and smb.conf is not the same ...")
-       else:
-               names.rootdn=res2[0]["rootDomainNamingContext"]
+       if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb, res2[0]["defaultNamingContext"][0]))):
+               raise ProvisioningError(("basedn in %s (%s) and from %s (%s) is not the same ..." % (paths.samdb, str(res2[0]["defaultNamingContext"][0]), paths.smbconf, basedn)))
+
+       names.domaindn=res2[0]["defaultNamingContext"]
+       names.rootdn=res2[0]["rootDomainNamingContext"]
        # default site name
        attrs3 = ["cn"]
        res3= samdb.search(expression="(objectClass=*)",base="CN=Sites,"+configdn, scope=SCOPE_ONELEVEL, attrs=attrs3)
@@ -205,43 +207,37 @@ def guess_names_from_current_provision(credentials,session_info,paths):
 
        # dns hostname and server dn
        attrs4 = ["dNSHostName"]
-       res4= samdb.search(expression="(CN=%s)"%names.netbiosname,base="OU=Domain Controllers,"+rootdn, \
+       res4= samdb.search(expression="(CN=%s)"%names.netbiosname,base="OU=Domain Controllers,"+basedn, \
                                                scope=SCOPE_ONELEVEL, attrs=attrs4)
        names.hostname = str(res4[0]["dNSHostName"]).replace("."+names.dnsdomain,"")
 
-       names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (names.netbiosname, names.sitename, configdn)
+       server_res = samdb.search(expression="serverReference=%s"%res4[0].dn, attrs=[], base=configdn)
+       names.serverdn = server_res[0].dn
+
+       # invocation id/objectguid
+       res5 = samdb.search(expression="(objectClass=*)",base="CN=NTDS Settings,%s" % str(names.serverdn), scope=SCOPE_BASE, attrs=["invocationID","objectGUID"])
+       names.invocation = str(ndr_unpack( misc.GUID,res5[0]["invocationId"][0]))
+       names.ntdsguid = str(ndr_unpack( misc.GUID,res5[0]["objectGUID"][0]))
 
-       # invocation id
-       attrs5 = ["invocationId"]
-       res5 = samdb.search(expression="(objectClass=*)",base="CN=Sites,"+configdn, scope=SCOPE_SUBTREE, attrs=attrs5)
-       for i in range(0,len(res5)):
-               if ( len(res5[i]) > 0):
-                       names.invocation = str(ndr_unpack( misc.GUID,res5[i]["invocationId"][0]))
-                       break
        # domain guid/sid
        attrs6 = ["objectGUID", "objectSid", ]
-       res6 = samdb.search(expression="(objectClass=*)",base=rootdn, scope=SCOPE_BASE, attrs=attrs6)
+       res6 = samdb.search(expression="(objectClass=*)",base=basedn, scope=SCOPE_BASE, attrs=attrs6)
        names.domainguid = str(ndr_unpack( misc.GUID,res6[0]["objectGUID"][0]))
        names.domainsid = str(ndr_unpack( security.dom_sid,res6[0]["objectSid"][0]))
 
        # policy guid
        attrs7 = ["cn","displayName"]
-       res7 = samdb.search(expression="(displayName=Default Domain Policy)",base="CN=Policies,CN=System,"+rootdn, \
+       res7 = samdb.search(expression="(displayName=Default Domain Policy)",base="CN=Policies,CN=System,"+basedn, \
                                                        scope=SCOPE_ONELEVEL, attrs=attrs7)
        names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
        # dc policy guid
        attrs8 = ["cn","displayName"]
-       res8 = samdb.search(expression="(displayName=Default Domain Controllers Policy)",base="CN=Policies,CN=System,"+rootdn, \
+       res8 = samdb.search(expression="(displayName=Default Domain Controllers Policy)",base="CN=Policies,CN=System,"+basedn, \
                                                        scope=SCOPE_ONELEVEL, attrs=attrs7)
        if len(res8) == 1:
                names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
        else:
                names.policyid_dc = None
-       # ntds guid
-       attrs9 = ["objectGUID" ]
-       exp = "(dn=CN=NTDS Settings,%s)"%(names.serverdn)
-       res9 = samdb.search(expression="(dn=CN=NTDS Settings,%s)"%(names.serverdn),base=str(names.configdn), scope=SCOPE_SUBTREE, attrs=attrs9)
-       names.ntdsguid = str(ndr_unpack( misc.GUID,res9[0]["objectGUID"][0]))
 
 
        return names
@@ -251,7 +247,7 @@ def print_names(names):
        message(GUESS, "rootdn      :"+str(names.rootdn))
        message(GUESS, "configdn    :"+str(names.configdn))
        message(GUESS, "schemadn    :"+str(names.schemadn))
-       message(GUESS, "serverdn    :"+names.serverdn)
+       message(GUESS, "serverdn    :"+str(names.serverdn))
        message(GUESS, "netbiosname :"+names.netbiosname)
        message(GUESS, "defaultsite :"+names.sitename)
        message(GUESS, "dnsdomain   :"+names.dnsdomain)
@@ -285,7 +281,7 @@ def newprovision(names,setup_dir,creds,session,smbconf):
                session, creds, smbconf=smbconf, targetdir=provdir,
                samdb_fill=FILL_FULL, realm=names.realm, domain=names.domain,
                domainguid=names.domainguid, domainsid=names.domainsid,ntdsguid=names.ntdsguid,
-               policyguid=names.policyid,policyguid_dc=names.policyid_dc,hostname=names.hostname,
+               policyguid=names.policyid,policyguid_dc=names.policyid_dc,hostname=names.netbiosname,
                hostip=None, hostip6=None,
                invocationid=names.invocation, adminpass=None,
                krbtgtpass=None, machinepass=None,
@@ -506,7 +502,7 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
                # The double ldb open and schema validation is taken from the initial provision script
                # it's not certain that it is really needed ....
                sam_ldb = Ldb(session_info=session, credentials=creds, lp=lp)
-               schema = Schema(setup_path, security.dom_sid(names.domainsid), schemadn=basedn, serverdn=names.serverdn)
+               schema = Schema(setup_path, security.dom_sid(names.domainsid), schemadn=basedn, serverdn=str(names.serverdn))
                # Load the schema from the one we computed earlier
                sam_ldb.set_schema_from_ldb(schema.ldb)
                # And now we can connect to the DB - the schema won't be loaded from the DB
@@ -686,6 +682,40 @@ def update_samdb(newpaths,paths,creds,session,names):
        message(SIMPLE,"Done with scanning")
 #      update_sds(hashdef,hashSD,paths,creds,session,str(names.rootdn),names.domainsid)
 
+def update_machine_account_password(newpaths,paths,creds,session,names):
+
+       secrets_ldb = Ldb(newpaths.secrets, session_info=session, credentials=creds,lp=lp)
+       secrets_ldb.transaction_start()
+       secrets_msg = secrets_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=["secureChannelType"])
+       sam_ldb = Ldb(paths.samdb, session_info=session, credentials=creds,lp=lp)
+       if secrets_msg[0]["secureChannelType"][0] == SEC_CHAN_BDC:
+               sam_ldb.transaction_start()
+               res = sam_ldb.search(expression=("samAccountName=%s$" % names.netbiosname), attrs=[])
+               assert(len(res) == 1)
+
+               msg = ldb.Message(res[0].dn)
+               machinepass = msg["userPassword"] = glue.generate_random_str(12)
+               for el in msg:
+                       el.set_flags(ldb.FLAG_MOD_REPLACE)
+               sam_ldb.modify(msg)
+
+               res = sam_ldb.search(expression=("samAccountName=%s$" % names.netbiosname),
+                                    attrs=["msDs-keyVersionNumber"])
+               assert(len(res) == 1)
+               kvno = res[0]["msDs-keyVersionNumber"]
+
+               secretsdb_self_join(secrets_ldb, domain=names.domain,
+                                   realm=names.realm,
+                                   dnsdomain=names.dnsdomain,
+                                   netbiosname=names.netbiosname,
+                                   machinepass=machinepass,
+                                   key_version_number=kvno,
+                                   secure_channel_type=secrets_msg[0]["secureChannelType"])
+       sam_ldb.transaction_prepare_commit()
+       secrets_ldb.transaction_prepare_commit()
+       sam_ldb.transaction_commit()
+       secrets_ldb.transaction_commit()
+
 # From here start the big steps of the program
 # First get files paths
 paths=get_paths(targetdir=opts.targetdir,smbconf=smbconf)
@@ -708,6 +738,7 @@ update_secrets(newpaths,paths,creds,session)
 update_privilege(newpaths,paths)
 if opts.full:
        update_samdb(newpaths,paths,creds,session,names)
+       update_machine_account_password(newpaths,paths,creds,session,names)
 message(SIMPLE,"Upgrade finished !")
 # remove reference provision now that everything is done !
 rmall(provisiondir)