upgradeprovision: mark rIDAvailablePool never upgraded
authorMatthieu Patou <mat@matws.net>
Mon, 18 Jan 2010 22:53:01 +0000 (01:53 +0300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 20 Jan 2010 18:11:20 +0000 (07:11 +1300)
 handle properly the fact that missing object might depend on some other in order to be correctly created
 debug change also if we are in debugall mode

source4/scripting/bin/upgradeprovision

index 0c33ee358ef98db75ced8fa3eaef4be697e1078b..1c70e377ae6ac9663774a9e2dae7e9b80bedfb6f 100755 (executable)
@@ -54,6 +54,7 @@ from samba.dcerpc import misc, security
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.dcerpc.misc import SEC_CHAN_BDC
 
+never=0
 replace=2^ldb.FLAG_MOD_REPLACE
 add=2^ldb.FLAG_MOD_ADD
 delete=2^ldb.FLAG_MOD_DELETE
@@ -85,11 +86,12 @@ hashAttrNotCopied = {       "dn": 1,"whenCreated": 1,"whenChanged": 1,"objectGUID": 1
 hashOverwrittenAtt = { "prefixMap": replace, "systemMayContain": replace,"systemOnly":replace, "searchFlags":replace,
                                                "mayContain":replace,  "systemFlags":replace,"description":replace,
                                                "oEMInformation":replace, "operatingSystemVersion":replace, "adminPropertyPages":replace,
-                                               "defaultSecurityDescriptor": replace,"wellKnownObjects":replace,"privilege":delete,"groupType":replace}
+                                               "defaultSecurityDescriptor": replace,"wellKnownObjects":replace,"privilege":delete,"groupType":replace,
+                                               "rIDAvailablePool": never}
 
 
 backlinked = []
-
+dn_syntax_att = []
 def define_what_to_log(opts):
        what = 0
        if opts.debugchange:
@@ -156,7 +158,15 @@ def identic_rename(ldbobj,dn):
 # Create an array of backlinked attributes
 def populate_backlink(newpaths,creds,session,schemadn):
        newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
-       backlinked.extend(get_linked_attributes(ldb.Dn(newsam_ldb,str(schemadn)),newsam_ldb).values())
+       linkedAttHash = get_linked_attributes(ldb.Dn(newsam_ldb,str(schemadn)),newsam_ldb)
+       backlinked.extend(linkedAttHash.values())
+
+# Create an array of  attributes with a dn synthax (2.5.5.1)
+def populate_dnsyntax(newpaths,creds,session,schemadn):
+       newsam_ldb = Ldb(newpaths.samdb, session_info=session, credentials=creds,lp=lp)
+       res = newsam_ldb.search(expression="(attributeSyntax=2.5.5.1)",base=ldb.Dn(newsam_ldb,str(schemadn)), scope=SCOPE_SUBTREE, attrs=["lDAPDisplayName"])
+       for elem in res:
+               dn_syntax_att.append(elem["lDAPDisplayName"])
 
 # Get Paths for important objects (ldb, keytabs ...)
 def get_paths(targetdir=None,smbconf=None):
@@ -497,6 +507,107 @@ def update_secrets(newpaths,paths,creds,session):
                delta.dn = current[0].dn
                secrets_ldb.modify(delta)
 
+def dump_denied_change(dn,att,flagtxt,current,reference):
+       message(CHANGE, "dn= "+str(dn)+" "+att+" with flag "+flagtxt+" is not allowed to be changed/removed, I discard this change ...")
+       if att != "objectSid" :
+               i = 0
+               for e in range(0,len(current)):
+                       message(CHANGE,"old %d : %s"%(i,str(current[e])))
+                       i=i+1
+               if reference != None:
+                       i = 0
+                       for e in range(0,len(reference)):
+                                       message(CHANGE,"new %d : %s"%(i,str(reference[e])))
+                                       i=i+1
+       else:
+               message(CHANGE,"old : %s"%str(ndr_unpack( security.dom_sid,current[0])))
+               message(CHANGE,"new : %s"%str(ndr_unpack( security.dom_sid,reference[0])))
+
+#This function is for doing case by case treatment on special object
+
+def handle_special_add(sam_ldb,dn,names):
+       dntoremove=None
+       if str(dn).lower() == ("CN=Certificate Service DCOM Access,CN=Builtin,%s"%names.rootdn).lower():
+               #This entry was misplaced lets remove it if it exists
+               dntoremove="CN=Certificate Service DCOM Access,CN=Users,%s"%names.rootdn
+
+       if str(dn).lower() == ("CN=Cryptographic Operators,CN=Builtin,%s"%names.rootdn).lower():
+               #This entry was misplaced lets remove it if it exists
+               dntoremove="CN=Cryptographic Operators,CN=Users,%s"%names.rootdn
+
+       if str(dn).lower() == ("CN=Event Log Readers,CN=Builtin,%s"%names.rootdn).lower():
+               #This entry was misplaced lets remove it if it exists
+               dntoremove="CN=Event Log Readers,CN=Users,%s"%names.rootdn
+
+       if dntoremove != None:
+               res = sam_ldb.search(expression="objectClass=*",base=dntoremove, scope=SCOPE_BASE,attrs=["dn"],controls=["search_options:1:2"])
+               if len(res) > 0:
+                       message(CHANGE,"Existing object %s must be replaced by %s, removing old object"%(dntoremove,str(dn)))
+                       sam_ldb.delete(res[0]["dn"])
+
+#Check if the one of the dn in the listdn will be created after the current dn
+#hash is indexed by dn to be created, with each key is associated the creation order
+#First dn to be created has the creation order 0, second has 1, ...
+#Index contain the current creation order
+def check_dn_nottobecreated(hash,index,listdn):
+       if listdn == None:
+               return None
+       for dn in listdn:
+               key = str(dn).lower()
+               if hash.has_key(key) and hash[key] > index:
+                       return str(dn)
+       return None
+
+#This function tries to add the missing object "dn" if this object depends on some others
+# the function returns 0, if the object was created 1 is returned
+def add_missing_object(newsam_ldb,sam_ldb,dn,names,basedn,hash,index):
+       handle_special_add(sam_ldb,dn,names)
+       reference = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn,
+                                       scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
+       empty = ldb.Message()
+       delta = sam_ldb.msg_diff(empty,reference[0])
+       for att in hashAttrNotCopied.keys():
+               delta.remove(att)
+       for att in backlinked:
+               delta.remove(att)
+       depend_on_yettobecreated = None
+       for att in dn_syntax_att:
+               depend_on_yet_tobecreated = check_dn_nottobecreated(hash,index,delta.get(str(att)))
+               if depend_on_yet_tobecreated != None:
+                       message(CHANGE,"Object %s depends on %s in attribute %s, delaying the creation"
+                                                       %(str(dn),depend_on_yet_tobecreated,str(att)))
+                       return 0
+       delta.dn = dn
+       message(CHANGE,"Object %s will be added"%dn)
+       sam_ldb.add(delta,["relax:0"])
+       return 1
+
+def gen_dn_index_hash(listMissing):
+       hash = {}
+       for i in range(0,len(listMissing)):
+               hash[str(listMissing[i]).lower()] = i
+       return hash
+
+def add_missing_entries(newsam_ldb,sam_ldb,names,basedn,list):
+       listMissing = []
+       listDefered = list
+
+       while(len(listDefered) != len(listMissing) and len(listDefered) > 0):
+               index = 0
+               listMissing = listDefered
+               listDefered = []
+               hashMissing = gen_dn_index_hash(listMissing)
+               for dn in listMissing:
+                       ret =  add_missing_object(newsam_ldb,sam_ldb,dn,names,basedn,hashMissing,index)
+                       index = index + 1
+                       if ret == 0:
+                               #DN can't be created because it depends on some other DN in the list
+                               listDefered.append(dn)
+       if len(listDefered) != 0:
+               raise ProvisioningError("Unable to insert missing elements: circular references")
+
+
+
 
 # Check difference between the current provision and the reference provision.
 # It looks for all objects which base DN is name. If ischema is "false" then
@@ -534,6 +645,7 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
 
        for k in hash_new.keys():
                if not hash.has_key(k):
+                       print hash_new[k]
                        listMissing.append(hash_new[k])
                else:
                        listPresent.append(hash_new[k])
@@ -564,19 +676,8 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
 
        sam_ldb.transaction_start()
 
-       empty = ldb.Message()
        message(SIMPLE,"There are %d missing objects"%(len(listMissing)))
-       for dn in listMissing:
-               reference = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
-               delta = sam_ldb.msg_diff(empty,reference[0])
-               for att in hashAttrNotCopied.keys():
-                       delta.remove(att)
-               for att in backlinked:
-                       delta.remove(att)
-               delta.dn = dn
-
-               sam_ldb.add(delta,["relax:0"])
-
+       add_missing_entries(newsam_ldb,sam_ldb,names,basedn,listMissing)
        changed = 0
        for dn in listPresent:
                reference = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
@@ -605,9 +706,12 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
                                delta.remove(att)
                                continue
                        if (not hashOverwrittenAtt.has_key(att) or not (hashOverwrittenAtt.get(att)&2^msgElt.flags())):
+                               if  hashOverwrittenAtt.has_key(att) and hashOverwrittenAtt.get(att)==never:
+                                       delta.remove(att)
+                                       continue
                                if  handle_special_case(att,delta,reference,current,ischema)==0 and msgElt.flags()!=ldb.FLAG_MOD_ADD:
                                        i = 0
-                                       if opts.debugchange:
+                                       if opts.debugchange or opts.debugall:
                                                try:
                                                        dump_denied_change(dn,att,messageEltFlagToString(msgElt.flags()),current[0][att],reference[0][att])
                                                except:
@@ -794,6 +898,7 @@ provisiondir = newprovision(names,setup_dir,creds,session,smbconf)
 # Get file paths of this new provision
 newpaths = get_paths(targetdir=provisiondir)
 populate_backlink(newpaths,creds,session,names.schemadn)
+populate_dnsyntax(newpaths,creds,session,names.schemadn)
 # Check the difference
 update_basesamdb(newpaths,paths,names)