samba_dnsupdate: make rodc_dns_update() more robust against timing problems
[samba.git] / source4 / scripting / bin / samba_upgradeprovision
index 7060b73f236aad60df32297195926bb9e961d39a..8433b4ddd9c6edf208b34a6c6918e307201046cf 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # vim: expandtab
 #
 # Copyright (C) Matthieu Patou <mat@matws.net> 2009 - 2010
@@ -36,17 +36,20 @@ sys.path.insert(0, "bin/python")
 import ldb
 import samba
 import samba.getopt as options
+from samba.samdb import get_default_backend_store
 
 from base64 import b64encode
 from samba.credentials import DONT_USE_KERBEROS
 from samba.auth import system_session, admin_session
+from samba import tdb_util
+from samba import mdb_util
 from ldb import (SCOPE_SUBTREE, SCOPE_BASE,
                 FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE,
                 MessageElement, Message, Dn, LdbError)
 from samba import param, dsdb, Ldb
 from samba.common import confirm
-from samba.provision import (get_domain_descriptor, find_provision_key_parameters,
-                            get_config_descriptor, get_empty_descriptor,
+from samba.descriptor import get_wellknown_sds, get_empty_descriptor, get_diff_sds
+from samba.provision import (find_provision_key_parameters,
                             ProvisioningError, get_last_provision_usn,
                             get_max_usn, update_provision_usn, setup_path)
 from samba.schema import get_linked_attributes, Schema, get_schema_descriptor
@@ -56,7 +59,7 @@ from samba.dcerpc.security import (
 from samba.ndr import ndr_unpack
 from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
                                  get_ldbs, findprovisionrange,
-                                 usn_in_range, identic_rename, get_diff_sddls,
+                                 usn_in_range, identic_rename,
                                  update_secrets, CHANGE, ERROR, SIMPLE,
                                  CHANGEALL, GUESS, CHANGESD, PROVISION,
                                  updateOEMInfo, getOEMInfo, update_gpo,
@@ -67,6 +70,7 @@ from samba.upgradehelpers import (dn_sort, get_paths, newprovision,
                                  increment_calculated_keyversion_number,
                                  print_provision_ranges)
 from samba.xattr import copytree_with_xattrs
+from samba.compat import cmp_to_key_fn
 
 # make sure the script dies immediately when hitting control-C,
 # rather than raising KeyboardInterrupt. As we do all database
@@ -93,7 +97,7 @@ __docformat__ = "restructuredText"
 # created
 # This also apply to imported object from reference provision
 replAttrNotCopied = [   "dn", "whenCreated", "whenChanged", "objectGUID",
-                        "parentGUID", "objectCategory", "distinguishedName",
+                        "parentGUID", "distinguishedName",
                         "instanceType", "cn",
                         "lmPwdHistory", "pwdLastSet", "ntPwdHistory",
                         "unicodePwd", "dBCSPwd", "supplementalCredentials",
@@ -121,7 +125,6 @@ hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,
                         "description":replace, "operatingSystemVersion":replace,
                         "adminPropertyPages":replace, "groupType":replace,
                         "wellKnownObjects":replace, "privilege":never,
-                        "defaultSecurityDescriptor": replace,
                         "rIDAvailablePool": never,
                         "versionNumber" : add,
                         "rIDNextRID": add, "rIDUsedPool": never,
@@ -130,7 +133,7 @@ hashOverwrittenAtt = {  "prefixMap": replace, "systemMayContain": replace,
                         "attributeDisplayNames": replace + add,
                         "versionNumber": add}
 
-dnNotToRecalculate = []
+dnNotToRecalculateFound = False
 dnToRecalculate = []
 backlinked = []
 forwardlinked = set()
@@ -168,16 +171,12 @@ parser.add_option("--debugchangesd", action="store_true",
                   help="Print security descriptor differences")
 parser.add_option("--debugall", action="store_true",
                   help="Print all available information (very verbose)")
-parser.add_option("--resetfileacl", action="store_true",
-                  help="Force a reset on filesystem acls in sysvol / netlogon share")
-parser.add_option("--nontaclfix", action="store_true",
-                  help="In full upgrade mode do not try to upgrade sysvol / netlogon acls")
-parser.add_option("--fixntacl", action="store_true",
-                  help="Only fix NT ACLs in sysvol / netlogon share")
 parser.add_option("--db_backup_only", action="store_true",
                   help="Do the backup of the database in the provision, skip the sysvol / netlogon shares")
 parser.add_option("--full", action="store_true",
                   help="Perform full upgrade of the samdb (schema, configuration, new objects, ...")
+parser.add_option("--very-old-pre-alpha9", action="store_true",
+                  help="Perform additional forced SD resets required for a database from before Samba 4.0.0alpha9.")
 
 opts = parser.parse_args()[0]
 
@@ -210,7 +209,7 @@ creds.set_kerberos_state(DONT_USE_KERBEROS)
 
 
 
-def check_for_DNS(refprivate, private):
+def check_for_DNS(refprivate, private, refbinddns_dir, binddns_dir, dns_backend):
     """Check if the provision has already the requirement for dynamic dns
 
     :param refprivate: The path to the private directory of the reference
@@ -220,10 +219,6 @@ def check_for_DNS(refprivate, private):
 
     spnfile = "%s/spn_update_list" % private
     dnsfile = "%s/dns_update_list" % private
-    namedfile = lp.get("dnsupdate:path")
-
-    if not namedfile:
-       namedfile = "%s/named.conf.update" % private
 
     if not os.path.exists(spnfile):
         shutil.copy("%s/spn_update_list" % refprivate, "%s" % spnfile)
@@ -231,16 +226,25 @@ def check_for_DNS(refprivate, private):
     if not os.path.exists(dnsfile):
         shutil.copy("%s/dns_update_list" % refprivate, "%s" % dnsfile)
 
-    destdir = "%s/new_dns" % private
-    dnsdir = "%s/dns" % private
+    if not os.path.exists(binddns_dir):
+        os.mkdir(binddns_dir)
 
+    if dns_backend not in ['BIND9_DLZ', 'BIND9_FLATFILE']:
+       return
+
+    namedfile = lp.get("dnsupdate:path")
+    if not namedfile:
+       namedfile = "%s/named.conf.update" % binddns_dir
     if not os.path.exists(namedfile):
+        destdir = "%s/new_dns" % binddns_dir
+        dnsdir = "%s/dns" % binddns_dir
+
         if not os.path.exists(destdir):
             os.mkdir(destdir)
         if not os.path.exists(dnsdir):
             os.mkdir(dnsdir)
-        shutil.copy("%s/named.conf" % refprivate, "%s/named.conf" % destdir)
-        shutil.copy("%s/named.txt" % refprivate, "%s/named.txt" % destdir)
+        shutil.copy("%s/named.conf" % refbinddns_dir, "%s/named.conf" % destdir)
+        shutil.copy("%s/named.txt" % refbinddns_dir, "%s/named.txt" % destdir)
         message(SIMPLE, "It seems that your provision did not integrate "
                 "new rules for dynamic dns update of domain related entries")
         message(SIMPLE, "A copy of the new bind configuration files and "
@@ -305,13 +309,13 @@ def sanitychecks(samdb, names):
                          scope=SCOPE_SUBTREE, attrs=["dn"],
                          controls=["search_options:1:2"])
     if len(res) == 0:
-        print "No DC found. Your provision is most probably broken!"
+        print("No DC found. Your provision is most probably broken!")
         return False
     elif len(res) != 1:
-        print "Found %d domain controllers. For the moment " \
+        print("Found %d domain controllers. For the moment " \
               "upgradeprovision is not able to handle an upgrade on a " \
               "domain with more than one DC. Please demote the other " \
-              "DC(s) before upgrading" % len(res)
+              "DC(s) before upgrading") % len(res)
         return False
     else:
         return True
@@ -373,7 +377,7 @@ def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
                             (int(str(old[0][att])), int(str(new[0][att]))))
             return False
         if (att == "minPwdAge" and flag == FLAG_MOD_REPLACE):
-            if (long(str(old[0][att])) == 0):
+            if (int(str(old[0][att])) == 0):
                 delta[att] = MessageElement(new[0][att], FLAG_MOD_REPLACE, att)
             return True
 
@@ -386,7 +390,7 @@ def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
                 newval.append(str(elem))
 
             for elem in new[0][att]:
-                if not hash.has_key(str(elem).lower()):
+                if not str(elem).lower() in hash:
                     changeDelta=1
                     newval.append(str(elem))
             if changeDelta == 1:
@@ -441,7 +445,7 @@ def handle_special_case(att, delta, new, old, useReplMetadata, basedn, aldb):
             newval.append(str(elem))
 
         for elem in new[0][att]:
-            if not hash.has_key(str(elem)):
+            if not str(elem) in hash:
                 changeDelta = 1
                 newval.append(str(elem))
         if changeDelta == 1:
@@ -579,7 +583,7 @@ def check_dn_nottobecreated(hash, index, listdn):
         return None
     for dn in listdn:
         key = str(dn).lower()
-        if hash.has_key(key) and hash[key] > index:
+        if key in hash and hash[key] > index:
             return str(dn)
     return None
 
@@ -626,7 +630,7 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
                     skip = True
     finally:
         if delta.get("objectSid"):
-            sid = str(ndr_unpack(security.dom_sid, str(reference[0]["objectSid"])))
+            sid = str(ndr_unpack(security.dom_sid, reference[0]["objectSid"][0]))
             m = re.match(r".*-(\d+)$", sid)
             if m and int(m.group(1))>999:
                 delta.remove("objectSid")
@@ -634,7 +638,6 @@ def add_missing_object(ref_samdb, samdb, dn, names, basedn, hash, index):
             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)))
@@ -796,7 +799,7 @@ def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
     # Also this function in fact just accept add not removal
 
     for e in res[0][att]:
-        if not hash.has_key(e):
+        if not e in hash:
             # We put in the blacklist all the element that are in the "revealed"
             # result and not in the "standard" result
             # This element are links that were removed before and so that
@@ -804,7 +807,7 @@ def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
             blacklist[e] = 1
 
     for e in ref_value:
-        if not blacklist.has_key(e) and not hash.has_key(e):
+        if not e in blacklist and not e in hash:
             newlinklist.append(str(e))
             changed = True
     if changed:
@@ -815,68 +818,6 @@ def handle_links(samdb, att, basedn, dn, value, ref_value, delta):
     return delta
 
 
-msg_elt_flag_strs = {
-    ldb.FLAG_MOD_ADD: "MOD_ADD",
-    ldb.FLAG_MOD_REPLACE: "MOD_REPLACE",
-    ldb.FLAG_MOD_DELETE: "MOD_DELETE" }
-
-def checkKeepAttributeOldMtd(delta, att, reference, current,
-                                    basedn, samdb):
-    """ Check if we should keep the attribute modification or not.
-        This function didn't use replicationMetadata to take a decision.
-
-        :param delta: A message diff object
-        :param att: An attribute
-        :param reference: A message object for the current entry comming from
-                            the reference provision.
-        :param current: A message object for the current entry commin from
-                            the current provision.
-        :param basedn: The DN of the partition
-        :param samdb: A ldb connection to the sam database of the current provision.
-
-        :return: The modified message diff.
-    """
-    # Old school way of handling things for pre alpha12 upgrade
-    global defSDmodified
-    isFirst = False
-    txt = ""
-    dn = current[0].dn
-
-    for att in list(delta):
-        msgElt = delta.get(att)
-
-        if att == "nTSecurityDescriptor":
-            defSDmodified = True
-            delta.remove(att)
-            continue
-
-        if att == "dn":
-            continue
-
-        if not hashOverwrittenAtt.has_key(att):
-            if msgElt.flags() != FLAG_MOD_ADD:
-                if not handle_special_case(att, delta, reference, current,
-                                            False, basedn, samdb):
-                    if opts.debugchange or opts.debugall:
-                        try:
-                            dump_denied_change(dn, att,
-                                msg_elt_flag_strs[msgElt.flags()],
-                                current[0][att], reference[0][att])
-                        except KeyError:
-                            dump_denied_change(dn, att,
-                                msg_elt_flag_strs[msgElt.flags()],
-                                current[0][att], None)
-                    delta.remove(att)
-                continue
-        else:
-            if hashOverwrittenAtt.get(att)&2**msgElt.flags() :
-                continue
-            elif hashOverwrittenAtt.get(att) == never:
-                delta.remove(att)
-                continue
-
-    return delta
-
 def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
                                     hash_attr_usn, basedn, usns, samdb):
     """ Check if we should keep the attribute modification or not
@@ -945,13 +886,11 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
 
         if att == "nTSecurityDescriptor":
             cursd = ndr_unpack(security.descriptor,
-                str(current[0]["nTSecurityDescriptor"]))
-            cursddl = cursd.as_sddl(names.domainsid)
+                current[0]["nTSecurityDescriptor"][0])
             refsd = ndr_unpack(security.descriptor,
-                str(reference[0]["nTSecurityDescriptor"]))
-            refsddl = refsd.as_sddl(names.domainsid)
+                reference[0]["nTSecurityDescriptor"][0])
 
-            diff = get_diff_sddls(refsddl, cursddl)
+            diff = get_diff_sds(refsd, cursd, names.domainsid)
             if diff == "":
                 # FIXME find a way to have it only with huge huge verbose mode
                 # message(CHANGE, "%ssd are identical" % txt)
@@ -966,9 +905,10 @@ def checkKeepAttributeWithMetadata(delta, att, message, reference, current,
                     message(CHANGESD, "But the SD has been changed by someonelse "
                                     "so it's impossible to know if the difference"
                                     " cames from the modification or from a previous bug")
-                    dnNotToRecalculate.append(str(dn))
+                    global dnNotToRecalculateFound
+                    dnNotToRecalculateFound = True
                 else:
-                    dnToRecalculate.append(str(dn))
+                    dnToRecalculate.append(dn)
                 continue
 
         if attrUSN == -1:
@@ -1036,8 +976,7 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns):
     changed = 0
     sd_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
     controls = ["search_options:1:2", "sd_flags:1:%d" % sd_flags]
-    if usns is not None:
-            message(CHANGE, "Using replPropertyMetadata for change selection")
+    message(CHANGE, "Using replPropertyMetadata for change selection")
     for dn in listPresent:
         reference = ref_samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
                                         scope=SCOPE_SUBTREE,
@@ -1072,13 +1011,13 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns):
         if nb_items == 1:
             continue
 
-        if nb_items > 1 and usns is not None:
+        if nb_items > 1:
             # Fetch the replPropertyMetaData
             res = samdb.search(expression="(distinguishedName=%s)" % (str(dn)), base=basedn,
                                 scope=SCOPE_SUBTREE, controls=controls,
                                 attrs=["replPropertyMetaData"])
             ctr = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
-                                str(res[0]["replPropertyMetaData"])).ctr
+                                res[0]["replPropertyMetaData"][0]).ctr
 
             hash_attr_usn = {}
             for o in ctr.array:
@@ -1090,12 +1029,9 @@ def update_present(ref_samdb, samdb, basedn, listPresent, usns):
                 else:
                     hash_attr_usn[att] = [-1, None]
 
-        if usns is not None:
-            delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
-                                                    current, hash_attr_usn,
-                                                    basedn, usns, samdb)
-        else:
-            delta =  checkKeepAttributeOldMtd(delta, att, reference, current, basedn, samdb)
+        delta = checkKeepAttributeWithMetadata(delta, att, message, reference,
+                                               current, hash_attr_usn,
+                                               basedn, usns, samdb)
 
         delta.dn = dn
 
@@ -1133,8 +1069,8 @@ def reload_full_schema(samdb, names):
     for ent in current:
         schema_ldif += samdb.write_ldif(ent, ldb.CHANGETYPE_NONE)
 
-    prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()
-    prefixmap_data = b64encode(prefixmap_data)
+    prefixmap_data = open(setup_path("prefixMap.txt"), 'rb').read()
+    prefixmap_data = b64encode(prefixmap_data).decode('utf8')
 
     # We don't actually add this ldif, just parse it
     prefixmap_ldif = "dn: %s\nprefixMap:: %s\n\n" % (schemadn, prefixmap_data)
@@ -1190,7 +1126,7 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre
 
 
     for k in hash_new.keys():
-        if not hash.has_key(k):
+        if not k in hash:
             if not str(hash_new[k]) == "CN=Deleted Objects, %s" % names.rootdn:
                 listMissing.append(hash_new[k])
         else:
@@ -1198,8 +1134,8 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre
 
     # Sort the missing object in order to have object of the lowest level
     # first (which can be containers for higher level objects)
-    listMissing.sort(dn_sort)
-    listPresent.sort(dn_sort)
+    listMissing.sort(key=cmp_to_key_fn(dn_sort))
+    listPresent.sort(key=cmp_to_key_fn(dn_sort))
 
     # The following lines is to load the up to
     # date schema into our current LDB
@@ -1224,7 +1160,7 @@ def update_partition(ref_samdb, samdb, basedn, names, schema, provisionUSNs, pre
         message(SIMPLE, "There are %d changed objects" % (changed))
         return 1
 
-    except StandardError, err:
+    except Exception as err:
         message(ERROR, "Exception during upgrade of samdb:")
         (typ, val, tb) = sys.exc_info()
         traceback.print_exception(typ, val, tb)
@@ -1250,127 +1186,91 @@ def check_updated_sd(ref_sam, cur_sam, names):
                                 controls=["search_options:1:2"])
     hash = {}
     for i in range(0, len(reference)):
-        refsd = ndr_unpack(security.descriptor,
-                    str(reference[i]["nTSecurityDescriptor"]))
-        hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid)
+        refsd_blob = reference[i]["nTSecurityDescriptor"][0]
+        hash[str(reference[i]["dn"]).lower()] = refsd_blob
 
 
     for i in range(0, len(current)):
         key = str(current[i]["dn"]).lower()
-        if hash.has_key(key):
+        if key in hash:
+            cursd_blob = current[i]["nTSecurityDescriptor"][0]
             cursd = ndr_unpack(security.descriptor,
-                        str(current[i]["nTSecurityDescriptor"]))
-            sddl = cursd.as_sddl(names.domainsid)
-            if sddl != hash[key]:
-                txt = get_diff_sddls(hash[key], sddl, False)
+                               cursd_blob)
+            if cursd_blob != hash[key]:
+                refsd = ndr_unpack(security.descriptor,
+                                   hash[key])
+                txt = get_diff_sds(refsd, cursd, names.domainsid, False)
                 if txt != "":
                     message(CHANGESD, "On object %s ACL is different"
                                       " \n%s" % (current[i]["dn"], txt))
 
 
 
-def fix_partition_sd(samdb, names):
-    """This function fix the SD for partition containers (basedn, configdn, ...)
+def fix_wellknown_sd(samdb, names):
+    """This function fix the SD for partition/wellknown containers (basedn, configdn, ...)
     This is needed because some provision use to have broken SD on containers
 
     :param samdb: An LDB object pointing to the sam of the current provision
     :param names: A list of key provision parameters
     """
-    alwaysRecalculate = False
-    if len(dnToRecalculate) == 0 and len(dnNotToRecalculate) == 0:
-        alwaysRecalculate = True
 
+    list_wellknown_dns = []
 
-    # NC's DN can't be both in dnToRecalculate and dnNotToRecalculate
-    # First update the SD for the rootdn
-    if alwaysRecalculate or str(names.rootdn) in dnToRecalculate:
-        delta = Message()
-        delta.dn = Dn(samdb, str(names.rootdn))
-        descr = get_domain_descriptor(names.domainsid)
-        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
-                                                        "nTSecurityDescriptor")
-        samdb.modify(delta)
+    subcontainers = get_wellknown_sds(samdb)
 
-    # Then the config dn
-    if alwaysRecalculate or str(names.configdn) in dnToRecalculate:
-        delta = Message()
-        delta.dn = Dn(samdb, str(names.configdn))
-        descr = get_config_descriptor(names.domainsid)
-        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
-                                                        "nTSecurityDescriptor" )
-        samdb.modify(delta)
+    for [dn, descriptor_fn] in subcontainers:
+        list_wellknown_dns.append(dn)
+        if dn in dnToRecalculate:
+            delta = Message()
+            delta.dn = dn
+            descr = descriptor_fn(names.domainsid, name_map=names.name_map)
+            delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
+                                                            "nTSecurityDescriptor" )
+            samdb.modify(delta)
+            message(CHANGESD, "nTSecurityDescriptor updated on wellknown DN: %s" % delta.dn)
 
-    # Then the schema dn
-    if alwaysRecalculate or str(names.schemadn) in dnToRecalculate:
-        delta = Message()
-        delta.dn = Dn(samdb, str(names.schemadn))
-        descr = get_schema_descriptor(names.domainsid)
-        delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
-                                                        "nTSecurityDescriptor" )
-        samdb.modify(delta)
+    return list_wellknown_dns
 
 def rebuild_sd(samdb, names):
     """Rebuild security descriptor of the current provision from scratch
 
-    During the different pre release of samba4 security descriptors (SD)
-    were notarly broken (up to alpha11 included)
-    This function allow to get them back in order, this function make the
-    assumption that nobody has modified manualy an SD
-    and so SD can be safely recalculated from scratch to get them right.
+    During the different pre release of samba4 security descriptors
+    (SD) were notarly broken (up to alpha11 included)
 
-    :param names: List of key provision parameters"""
+    This function allows one to get them back in order, this function works
+    only after the database comparison that --full mode uses and which
+    populates the dnToRecalculate and dnNotToRecalculate lists.
 
-    fix_partition_sd(samdb, names)
+    The idea is that the SD can be safely recalculated from scratch to get it right.
 
-    # List of namming contexts
-    listNC = [str(names.rootdn), str(names.configdn), str(names.schemadn)]
-    hash = {}
-    if len(dnToRecalculate) == 0:
-        res = samdb.search(expression="objectClass=*", base=str(names.rootdn),
-                        scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"],
-                        controls=["search_options:1:2"])
-        for obj in res:
-                hash[str(obj["dn"])] = obj["whenCreated"]
-    else:
-        for dn in dnToRecalculate:
-            if hash.has_key(dn):
-                continue
-            # fetch each dn to recalculate and their child within the same partition
-            res = samdb.search(expression="objectClass=*", base=dn,
-                        scope=SCOPE_SUBTREE, attrs=["dn", "whenCreated"])
-            for obj in res:
-                hash[str(obj["dn"])] = obj["whenCreated"]
+    :param names: List of key provision parameters"""
 
-    listKeys = list(set(hash.keys()))
-    listKeys.sort(dn_sort)
+    listWellknown = fix_wellknown_sd(samdb, names)
 
     if len(dnToRecalculate) != 0:
         message(CHANGESD, "%d DNs have been marked as needed to be recalculated"
-                            ", recalculating %d due to inheritance"
-                            % (len(dnToRecalculate), len(listKeys)))
+                            % (len(dnToRecalculate)))
 
-    for key in listKeys:
-        if (key in listNC or
-                    key in dnNotToRecalculate):
+    for dn in dnToRecalculate:
+        # well known SDs have already been reset
+        if dn in listWellknown:
             continue
         delta = Message()
-        delta.dn = Dn(samdb, key)
+        delta.dn = dn
         sd_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_SACL
         try:
-            delta["whenCreated"] = MessageElement(hash[key], FLAG_MOD_REPLACE,
-                                                    "whenCreated" )
             descr = get_empty_descriptor(names.domainsid)
             delta["nTSecurityDescriptor"] = MessageElement(descr, FLAG_MOD_REPLACE,
                                                     "nTSecurityDescriptor")
-            samdb.modify(delta, ["sd_flags:1:%d" % sd_flags,"relax:0"])
-        except LdbError, e:
+            samdb.modify(delta, ["sd_flags:1:%d" % sd_flags,"relax:0","local_oid:%s:0" % dsdb.DSDB_CONTROL_DBCHECK])
+        except LdbError as e:
             samdb.transaction_cancel()
             res = samdb.search(expression="objectClass=*", base=str(delta.dn),
                                 scope=SCOPE_BASE,
                                 attrs=["nTSecurityDescriptor"],
                                 controls=["sd_flags:1:%d" % sd_flags])
             badsd = ndr_unpack(security.descriptor,
-                        str(res[0]["nTSecurityDescriptor"]))
+                        res[0]["nTSecurityDescriptor"][0])
             message(ERROR, "On %s bad stuff %s" % (str(delta.dn),badsd.as_sddl(names.domainsid)))
             return
 
@@ -1420,7 +1320,7 @@ def simple_update_basesamdb(newpaths, paths, names):
     :param names: List of key provision parameters"""
 
     message(SIMPLE, "Copy samdb")
-    shutil.copy(newpaths.samdb, paths.samdb)
+    tdb_util.tdb_copy(newpaths.samdb, paths.samdb)
 
     message(SIMPLE, "Update partitions filename if needed")
     schemaldb = os.path.join(paths.private_dir, "schema.ldb")
@@ -1430,33 +1330,21 @@ def simple_update_basesamdb(newpaths, paths, names):
 
     if not os.path.isdir(samldbdir):
         os.mkdir(samldbdir)
-        os.chmod(samldbdir, 0700)
+        os.chmod(samldbdir, 0o700)
     if os.path.isfile(schemaldb):
-        shutil.copy(schemaldb, os.path.join(samldbdir,
+        tdb_util.tdb_copy(schemaldb, os.path.join(samldbdir,
                                             "%s.ldb"%str(names.schemadn).upper()))
         os.remove(schemaldb)
     if os.path.isfile(usersldb):
-        shutil.copy(usersldb, os.path.join(samldbdir,
+        tdb_util.tdb_copy(usersldb, os.path.join(samldbdir,
                                             "%s.ldb"%str(names.rootdn).upper()))
         os.remove(usersldb)
     if os.path.isfile(configldb):
-        shutil.copy(configldb, os.path.join(samldbdir,
+        tdb_util.tdb_copy(configldb, os.path.join(samldbdir,
                                             "%s.ldb"%str(names.configdn).upper()))
         os.remove(configldb)
 
 
-def update_privilege(ref_private_path, cur_private_path):
-    """Update the privilege database
-
-    :param ref_private_path: Path to the private directory of the reference
-                             provision.
-    :param cur_private_path: Path to the private directory of the current
-                             (and to be updated) provision."""
-    message(SIMPLE, "Copy privilege")
-    shutil.copy(os.path.join(ref_private_path, "privilege.ldb"),
-                os.path.join(cur_private_path, "privilege.ldb"))
-
-
 def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
     """Upgrade the SAM DB contents for all the provision partitions
 
@@ -1483,7 +1371,7 @@ def update_samdb(ref_samdb, samdb, names, provisionUSNs, schema, prereloadfunc):
         return 0
 
 
-def backup_provision(paths, dir, only_db):
+def backup_provision(samdb, paths, dir, only_db):
     """This function backup the provision files so that a rollback
     is possible
 
@@ -1491,14 +1379,26 @@ def backup_provision(paths, dir, only_db):
     :param dir: Directory where to store the backup
     :param only_db: Skip sysvol for users with big sysvol
     """
+
+    # Currently we default to tdb for the backend store type
+    #
+    backend_store = "tdb"
+    res = samdb.search(base="@PARTITION",
+                       scope=ldb.SCOPE_BASE,
+                       attrs=["backendStore"])
+    if "backendStore" in res[0]:
+        backend_store = str(res[0]["backendStore"][0])
+
+
     if paths.sysvol and not only_db:
         copytree_with_xattrs(paths.sysvol, os.path.join(dir, "sysvol"))
-    shutil.copy2(paths.samdb, dir)
-    shutil.copy2(paths.secrets, dir)
-    shutil.copy2(paths.idmapdb, dir)
-    shutil.copy2(paths.privilege, dir)
+
+    tdb_util.tdb_copy(paths.samdb, os.path.join(dir, os.path.basename(paths.samdb)))
+    tdb_util.tdb_copy(paths.secrets, os.path.join(dir, os.path.basename(paths.secrets)))
+    tdb_util.tdb_copy(paths.idmapdb, os.path.join(dir, os.path.basename(paths.idmapdb)))
+    tdb_util.tdb_copy(paths.privilege, os.path.join(dir, os.path.basename(paths.privilege)))
     if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")):
-        shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir)
+        tdb_util.tdb_copy(os.path.join(paths.private_dir,"eadb.tdb"), os.path.join(dir, "eadb.tdb"))
     shutil.copy2(paths.smbconf, dir)
     shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir)
 
@@ -1508,11 +1408,20 @@ def backup_provision(paths, dir, only_db):
         schemaldb = os.path.join(paths.private_dir, "schema.ldb")
         configldb = os.path.join(paths.private_dir, "configuration.ldb")
         usersldb = os.path.join(paths.private_dir, "users.ldb")
-        shutil.copy2(schemaldb, dir)
-        shutil.copy2(usersldb, dir)
-        shutil.copy2(configldb, dir)
+        tdb_util.tdb_copy(schemaldb, os.path.join(dir, "schema.ldb"))
+        tdb_util.tdb_copy(usersldb, os.path.join(dir, "configuration.ldb"))
+        tdb_util.tdb_copy(configldb, os.path.join(dir, "users.ldb"))
     else:
-        shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))
+        os.mkdir(os.path.join(dir, "sam.ldb.d"), 0o700)
+
+        for ldb_name in os.listdir(samldbdir):
+            if not ldb_name.endswith("-lock"):
+                if backend_store == "mdb" and ldb_name != "metadata.tdb":
+                    mdb_util.mdb_copy(os.path.join(samldbdir, ldb_name),
+                                      os.path.join(dir, "sam.ldb.d", ldb_name))
+                else:
+                    tdb_util.tdb_copy(os.path.join(samldbdir, ldb_name),
+                                      os.path.join(dir, "sam.ldb.d", ldb_name))
 
 
 def sync_calculated_attributes(samdb, names):
@@ -1527,7 +1436,7 @@ def sync_calculated_attributes(samdb, names):
    """
    listAttrs = ["msDs-KeyVersionNumber"]
    hash = search_constructed_attrs_stored(samdb, names.rootdn, listAttrs)
-   if hash.has_key("msDs-KeyVersionNumber"):
+   if "msDs-KeyVersionNumber" in hash:
        increment_calculated_keyversion_number(samdb, names.rootdn,
                                             hash["msDs-KeyVersionNumber"])
 
@@ -1543,13 +1452,13 @@ def sync_calculated_attributes(samdb, names):
 # 6) get reference provision paths
 # 7) open reference provision ldbs
 # 8) setup helpers data that will help the update process
-# 9) update the privilege ldb by copying the one of referecence provision to
-#    the current provision
+# 9) (SKIPPED) we no longer update the privilege ldb by copying the one of referecence provision to
+#    the current provision, because a shutil.copy would break the transaction locks both databases are under
+#    and this database has not changed between 2009 and Samba 4.0.3 in Feb 2013 (at least)
 # 10)get the oemInfo field, this field contains information about the different
 #    provision that have been done
-# 11)Depending  on whether oemInfo has the string "alpha9" or alphaxx (x as an
-#    integer) or none of this the following things are done
-#    A) When alpha9 or alphaxx is present
+# 11)Depending on if the --very-old-pre-alpha9 flag is set the following things are done
+#    A) When alpha9 or alphaxx not specified (default)
 #       The base sam.ldb file is updated by looking at the difference between
 #       referrence one and the current one. Everything is copied with the
 #       exception of lastProvisionUSN attributes.
@@ -1617,7 +1526,7 @@ def sync_calculated_attributes(samdb, names):
 # The object is reappended at the end of the list to be created later
 # (and preferably after all the needed object have been created)
 # The function keeps on looping on the list of object to be created until
-# it's empty or that the number of defered creation is equal to the number
+# it's empty or that the number of deferred creation is equal to the number
 # of object that still needs to be created.
 
 # The function add_missing_object will first check if the object can be created.
@@ -1670,8 +1579,6 @@ if __name__ == '__main__':
     global defSDmodified
     defSDmodified = False
 
-    if opts.nontaclfix and opts.fixntacl:
-        message(SIMPLE, "nontaclfix and fixntacl are mutally exclusive")
     # From here start the big steps of the program
     # 1) First get files paths
     paths = get_paths(param, smbconf=smbconf)
@@ -1685,7 +1592,7 @@ if __name__ == '__main__':
     ldbs = get_ldbs(paths, creds, session, lp)
     backupdir = tempfile.mkdtemp(dir=paths.private_dir,
                                     prefix="backupprovision")
-    backup_provision(paths, backupdir, opts.db_backup_only)
+    backup_provision(ldbs.sam, paths, backupdir, opts.db_backup_only)
     try:
         ldbs.startTransactions()
 
@@ -1731,226 +1638,214 @@ if __name__ == '__main__':
         adm_session = admin_session(lp, str(names.domainsid))
         # So we reget handle on objects
         # ldbs = get_ldbs(paths, creds, adm_session, lp)
-        if not opts.fixntacl:
-            if not sanitychecks(ldbs.sam, names):
-                message(SIMPLE, "Sanity checks for the upgrade have failed. "
-                        "Check the messages and correct the errors "
-                        "before rerunning upgradeprovision")
-                ldbs.groupedRollback()
-                sys.exit(1)
 
-            # Let's see provision parameters
-            print_provision_key_parameters(names)
-
-            # 5) With all this information let's create a fresh new provision used as
-            # reference
-            message(SIMPLE, "Creating a reference provision")
-            provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
-                            prefix="referenceprovision")
-            result = newprovision(names, creds, session, smbconf, provisiondir,
-                    provision_logger)
-            result.report_logger(provision_logger)
-
-            # TODO
-            # 6) and 7)
-            # We need to get a list of object which SD is directly computed from
-            # defaultSecurityDescriptor.
-            # This will allow us to know which object we can rebuild the SD in case
-            # of change of the parent's SD or of the defaultSD.
-            # Get file paths of this new provision
-            newpaths = get_paths(param, targetdir=provisiondir)
-            new_ldbs = get_ldbs(newpaths, creds, session, lp)
-            new_ldbs.startTransactions()
-
-            populateNotReplicated(new_ldbs.sam, names.schemadn)
-            # 8) Populate some associative array to ease the update process
-            # List of attribute which are link and backlink
-            populate_links(new_ldbs.sam, names.schemadn)
-            # List of attribute with ASN DN synthax)
-            populate_dnsyntax(new_ldbs.sam, names.schemadn)
-            # 9)
-            update_privilege(newpaths.private_dir, paths.private_dir)
-            # 10)
-            oem = getOEMInfo(ldbs.sam, str(names.rootdn))
-            # Do some modification on sam.ldb
-            ldbs.groupedCommit()
-            new_ldbs.groupedCommit()
-            deltaattr = None
-        # 11)
-            message(GUESS, oem)
-            if oem is None or hasATProvision(ldbs.sam) or re.match(".*alpha((9)|(\d\d+)).*", str(oem)):
-                # 11) A
-                # Starting from alpha9 we can consider that the structure is quite ok
-                # and that we should do only dela
-                deltaattr = delta_update_basesamdb(newpaths.samdb,
-                                paths.samdb,
-                                creds,
-                                session,
-                                lp,
-                                message)
-            else:
-                # 11) B
-                simple_update_basesamdb(newpaths, paths, names)
-                ldbs = get_ldbs(paths, creds, session, lp)
-                removeProvisionUSN(ldbs.sam)
-
-            ldbs.startTransactions()
-            minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
-            new_ldbs.startTransactions()
-
-            # 12)
-            schema = Schema(names.domainsid, schemadn=str(names.schemadn))
-            # We create a closure that will be invoked just before schema reload
-            def schemareloadclosure():
-                basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
-                        options=["modules:"])
-                doit = False
-                if deltaattr is not None and len(deltaattr) > 1:
-                    doit = True
-                if doit:
-                    deltaattr.remove("dn")
-                    for att in deltaattr:
-                        if att.lower() == "dn":
-                            continue
-                        if (deltaattr.get(att) is not None
-                            and deltaattr.get(att).flags() != FLAG_MOD_ADD):
-                            doit = False
-                        elif deltaattr.get(att) is None:
-                            doit = False
-                if doit:
-                    message(CHANGE, "Applying delta to @ATTRIBUTES")
-                    deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
-                    basesam.modify(deltaattr)
-                else:
-                    message(CHANGE, "Not applying delta to @ATTRIBUTES because "
-                        "there is not only add")
-            # 13)
-            if opts.full:
-                if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
-                        schema, schemareloadclosure):
-                    message(SIMPLE, "Rolling back all changes. Check the cause"
-                            " of the problem")
-                    message(SIMPLE, "Your system is as it was before the upgrade")
-                    ldbs.groupedRollback()
-                    new_ldbs.groupedRollback()
-                    shutil.rmtree(provisiondir)
-                    sys.exit(1)
-            else:
-                # Try to reapply the change also when we do not change the sam
-                # as the delta_upgrade
-                schemareloadclosure()
-                sync_calculated_attributes(ldbs.sam, names)
-                res = ldbs.sam.search(expression="(samaccountname=dns)",
-                        scope=SCOPE_SUBTREE, attrs=["dn"],
-                        controls=["search_options:1:2"])
-                if len(res) > 0:
-                    message(SIMPLE, "You still have the old DNS object for managing "
-                            "dynamic DNS, but you didn't supply --full so "
-                            "a correct update can't be done")
-                    ldbs.groupedRollback()
-                    new_ldbs.groupedRollback()
-                    shutil.rmtree(provisiondir)
-                    sys.exit(1)
-            # 14)
-            update_secrets(new_ldbs.secrets, ldbs.secrets, message)
-            # 14bis)
-            res = ldbs.sam.search(expression="(samaccountname=dns)",
-                        scope=SCOPE_SUBTREE, attrs=["dn"],
-                        controls=["search_options:1:2"])
-
-            if (len(res) == 1):
-                ldbs.sam.delete(res[0]["dn"])
-                res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
-                        scope=SCOPE_SUBTREE, attrs=["dn"])
-                update_dns_account_password(ldbs.sam, ldbs.secrets, names)
-                message(SIMPLE, "IMPORTANT!!! "
-                        "If you were using Dynamic DNS before you need "
-                        "to update your configuration, so that the "
-                        "tkey-gssapi-credential has the following value: "
-                        "DNS/%s.%s" % (names.netbiosname.lower(),
-                            names.realm.lower()))
-            # 15)
-            message(SIMPLE, "Update machine account")
-            update_machine_account_password(ldbs.sam, ldbs.secrets, names)
-
-            dnToRecalculate.sort(dn_sort)
-            # 16) SD should be created with admin but as some previous acl were so wrong
-            # that admin can't modify them we have first to recreate them with the good
-            # form but with system account and then give the ownership to admin ...
-            if str(oem) != "" and not re.match(r'.*alpha(9|\d\d+)', str(oem)):
-                message(SIMPLE, "Fixing very old provision SD")
-                rebuild_sd(ldbs.sam, names)
-
-            # We calculate the max USN before recalculating the SD because we might
-            # touch object that have been modified after a provision and we do not
-            # want that the next upgradeprovision thinks that it has a green light
-            # to modify them
-
-            # 17)
-            maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
-
-            # 18) We rebuild SD if a we have a list of DN to recalculate or if the
-            # defSDmodified is set.
-            if defSDmodified or len(dnToRecalculate) >0:
-                message(SIMPLE, "Some (default) security descriptors (SDs) have "
-                                "changed, recalculating them")
-                ldbs.sam.set_session_info(adm_session)
-                rebuild_sd(ldbs.sam, names)
-
-            # 19)
-            # Now we are quite confident in the recalculate process of the SD, we make
-            # it optional. And we don't do it if there is DN that we must touch
-            # as we are assured that on this DNs we will have differences !
-            # Also the check must be done in a clever way as for the moment we just
-            # compare SDDL
-            if len(dnNotToRecalculate) == 0 and (opts.debugchangesd or opts.debugall):
-                message(CHANGESD, "Checking recalculated SDs")
-                check_updated_sd(new_ldbs.sam, ldbs.sam, names)
-
-            # 20)
-            updateOEMInfo(ldbs.sam, str(names.rootdn))
-            # 21)
-            check_for_DNS(newpaths.private_dir, paths.private_dir)
-            # 22)
-            if lastProvisionUSNs is not None:
-                update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
-            if opts.full and (names.policyid is None or names.policyid_dc is None):
-                update_policyids(names, ldbs.sam)
-        if opts.nontaclfix:
-            if opts.full or opts.resetfileacl or opts.fixntacl:
-                try:
-                    update_gpo(paths, ldbs.sam, names, lp, message, 1)
-                except ProvisioningError, e:
-                    message(ERROR, "The policy for domain controller is missing. "
-                                "You should restart upgradeprovision with --full")
-                except IOError, e:
-                    message(ERROR, "Setting ACL not supported on your filesystem")
+        if not sanitychecks(ldbs.sam, names):
+            message(SIMPLE, "Sanity checks for the upgrade have failed. "
+                    "Check the messages and correct the errors "
+                    "before rerunning upgradeprovision")
+            ldbs.groupedRollback()
+            sys.exit(1)
+
+        # Let's see provision parameters
+        print_provision_key_parameters(names)
+
+        # 5) With all this information let's create a fresh new provision used as
+        # reference
+        message(SIMPLE, "Creating a reference provision")
+        provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
+                        prefix="referenceprovision")
+        result = newprovision(names, session, smbconf, provisiondir,
+                provision_logger, base_schema="2008_R2")
+        result.report_logger(provision_logger)
+
+        # TODO
+        # 6) and 7)
+        # We need to get a list of object which SD is directly computed from
+        # defaultSecurityDescriptor.
+        # This will allow us to know which object we can rebuild the SD in case
+        # of change of the parent's SD or of the defaultSD.
+        # Get file paths of this new provision
+        newpaths = get_paths(param, targetdir=provisiondir)
+        new_ldbs = get_ldbs(newpaths, creds, session, lp)
+        new_ldbs.startTransactions()
+
+        populateNotReplicated(new_ldbs.sam, names.schemadn)
+        # 8) Populate some associative array to ease the update process
+        # List of attribute which are link and backlink
+        populate_links(new_ldbs.sam, names.schemadn)
+        # List of attribute with ASN DN synthax)
+        populate_dnsyntax(new_ldbs.sam, names.schemadn)
+        # 9) (now skipped, was copy of privileges.ldb)
+        # 10)
+        oem = getOEMInfo(ldbs.sam, str(names.rootdn))
+        # Do some modification on sam.ldb
+        ldbs.groupedCommit()
+        new_ldbs.groupedCommit()
+        deltaattr = None
+    # 11)
+        message(GUESS, oem)
+        if oem is None or hasATProvision(ldbs.sam) or not opts.very_old_pre_alpha9:
+            # 11) A
+            # Starting from alpha9 we can consider that the structure is quite ok
+            # and that we should do only dela
+            deltaattr = delta_update_basesamdb(newpaths.samdb,
+                            paths.samdb,
+                            creds,
+                            session,
+                            lp,
+                            message)
+        else:
+            # 11) B
+            simple_update_basesamdb(newpaths, paths, names)
+            ldbs = get_ldbs(paths, creds, session, lp)
+            removeProvisionUSN(ldbs.sam)
+
+        ldbs.startTransactions()
+        minUSN = int(str(get_max_usn(ldbs.sam, str(names.rootdn)))) + 1
+        new_ldbs.startTransactions()
+
+        # 12)
+        schema = Schema(names.domainsid, schemadn=str(names.schemadn))
+        # We create a closure that will be invoked just before schema reload
+        def schemareloadclosure():
+            basesam = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp,
+                    options=["modules:"])
+            doit = False
+            if deltaattr is not None and len(deltaattr) > 1:
+                doit = True
+            if doit:
+                deltaattr.remove("dn")
+                for att in deltaattr:
+                    if att.lower() == "dn":
+                        continue
+                    if (deltaattr.get(att) is not None
+                        and deltaattr.get(att).flags() != FLAG_MOD_ADD):
+                        doit = False
+                    elif deltaattr.get(att) is None:
+                        doit = False
+            if doit:
+                message(CHANGE, "Applying delta to @ATTRIBUTES")
+                deltaattr.dn = ldb.Dn(basesam, "@ATTRIBUTES")
+                basesam.modify(deltaattr)
             else:
-                try:
-                    update_gpo(paths, ldbs.sam, names, lp, message, 0)
-                except ProvisioningError, e:
-                    message(ERROR, "The policy for domain controller is missing. "
-                                "You should restart upgradeprovision with --full")
-        if not opts.fixntacl:
-            ldbs.groupedCommit()
-            new_ldbs.groupedCommit()
-            message(SIMPLE, "Upgrade finished!")
-            # remove reference provision now that everything is done !
-            # So we have reindexed first if need when the merged schema was reloaded
-            # (as new attributes could have quick in)
-            # But the second part of the update (when we update existing objects
-            # can also have an influence on indexing as some attribute might have their
-            # searchflag modificated
-            message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
-                    "after modification")
-            samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
-            message(SIMPLE, "Reindexing finished")
-
-            shutil.rmtree(provisiondir)
+                message(CHANGE, "Not applying delta to @ATTRIBUTES because "
+                    "there is not only add")
+        # 13)
+        if opts.full:
+            if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs,
+                    schema, schemareloadclosure):
+                message(SIMPLE, "Rolling back all changes. Check the cause"
+                        " of the problem")
+                message(SIMPLE, "Your system is as it was before the upgrade")
+                ldbs.groupedRollback()
+                new_ldbs.groupedRollback()
+                shutil.rmtree(provisiondir)
+                sys.exit(1)
         else:
-            ldbs.groupedRollback()
-            message(SIMPLE, "ACLs fixed !")
-    except StandardError, err:
+            # Try to reapply the change also when we do not change the sam
+            # as the delta_upgrade
+            schemareloadclosure()
+            sync_calculated_attributes(ldbs.sam, names)
+            res = ldbs.sam.search(expression="(samaccountname=dns)",
+                    scope=SCOPE_SUBTREE, attrs=["dn"],
+                    controls=["search_options:1:2"])
+            if len(res) > 0:
+                message(SIMPLE, "You still have the old DNS object for managing "
+                        "dynamic DNS, but you didn't supply --full so "
+                        "a correct update can't be done")
+                ldbs.groupedRollback()
+                new_ldbs.groupedRollback()
+                shutil.rmtree(provisiondir)
+                sys.exit(1)
+        # 14)
+        update_secrets(new_ldbs.secrets, ldbs.secrets, message)
+        # 14bis)
+        res = ldbs.sam.search(expression="(samaccountname=dns)",
+                    scope=SCOPE_SUBTREE, attrs=["dn"],
+                    controls=["search_options:1:2"])
+
+        if (len(res) == 1):
+            ldbs.sam.delete(res[0]["dn"])
+            res2 = ldbs.secrets.search(expression="(samaccountname=dns)",
+                    scope=SCOPE_SUBTREE, attrs=["dn"])
+            update_dns_account_password(ldbs.sam, ldbs.secrets, names)
+            message(SIMPLE, "IMPORTANT!!! "
+                    "If you were using Dynamic DNS before you need "
+                    "to update your configuration, so that the "
+                    "tkey-gssapi-credential has the following value: "
+                    "DNS/%s.%s" % (names.netbiosname.lower(),
+                        names.realm.lower()))
+        # 15)
+        message(SIMPLE, "Update machine account")
+        update_machine_account_password(ldbs.sam, ldbs.secrets, names)
+
+        # 16) SD should be created with admin but as some previous acl were so wrong
+        # that admin can't modify them we have first to recreate them with the good
+        # form but with system account and then give the ownership to admin ...
+        if opts.very_old_pre_alpha9:
+            message(SIMPLE, "Fixing very old provision SD")
+            rebuild_sd(ldbs.sam, names)
+
+        # We calculate the max USN before recalculating the SD because we might
+        # touch object that have been modified after a provision and we do not
+        # want that the next upgradeprovision thinks that it has a green light
+        # to modify them
+
+        # 17)
+        maxUSN = get_max_usn(ldbs.sam, str(names.rootdn))
+
+        # 18) We rebuild SD if a we have a list of DN to recalculate or if the
+        # defSDmodified is set.
+        if opts.full and (defSDmodified or len(dnToRecalculate) >0):
+            message(SIMPLE, "Some (default) security descriptors (SDs) have "
+                            "changed, recalculating them")
+            ldbs.sam.set_session_info(adm_session)
+            rebuild_sd(ldbs.sam, names)
+
+        # 19)
+        # Now we are quite confident in the recalculate process of the SD, we make
+        # it optional. And we don't do it if there is DN that we must touch
+        # as we are assured that on this DNs we will have differences !
+        # Also the check must be done in a clever way as for the moment we just
+        # compare SDDL
+        if dnNotToRecalculateFound == False and (opts.debugchangesd or opts.debugall):
+            message(CHANGESD, "Checking recalculated SDs")
+            check_updated_sd(new_ldbs.sam, ldbs.sam, names)
+
+        # 20)
+        updateOEMInfo(ldbs.sam, str(names.rootdn))
+        # 21)
+        check_for_DNS(newpaths.private_dir, paths.private_dir,
+                      newpaths.binddns_dir, paths.binddns_dir,
+                      names.dns_backend)
+        # 22)
+        update_provision_usn(ldbs.sam, minUSN, maxUSN, names.invocation)
+        if opts.full and (names.policyid is None or names.policyid_dc is None):
+            update_policyids(names, ldbs.sam)
+
+        if opts.full:
+            try:
+                update_gpo(paths, ldbs.sam, names, lp, message)
+            except ProvisioningError as e:
+                message(ERROR, "The policy for domain controller is missing. "
+                            "You should restart upgradeprovision with --full")
+
+        ldbs.groupedCommit()
+        new_ldbs.groupedCommit()
+        message(SIMPLE, "Upgrade finished!")
+        # remove reference provision now that everything is done !
+        # So we have reindexed first if need when the merged schema was reloaded
+        # (as new attributes could have quick in)
+        # But the second part of the update (when we update existing objects
+        # can also have an influence on indexing as some attribute might have their
+        # searchflag modificated
+        message(SIMPLE, "Reopening samdb to trigger reindexing if needed "
+                "after modification")
+        samdb = Ldb(paths.samdb, session_info=session, credentials=creds, lp=lp)
+        message(SIMPLE, "Reindexing finished")
+
+        shutil.rmtree(provisiondir)
+    except Exception as err:
         message(ERROR, "A problem occurred while trying to upgrade your "
                    "provision. A full backup is located at %s" % backupdir)
         if opts.debugall or opts.debugchange: