From b9f95882f0fd9f453c6b90d1ca023111195d757b Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 26 Nov 2009 15:32:49 +1100 Subject: s4:upgradeprovision Rework script, and reset machine account pw 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 | 85 +++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'source4') diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 74d9829376..7dd9a95b1e 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -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) -- cgit