diff options
Diffstat (limited to 'source4/scripting/bin/upgradeprovision')
| -rwxr-xr-x | source4/scripting/bin/upgradeprovision | 420 | 
1 files changed, 268 insertions, 152 deletions
diff --git a/source4/scripting/bin/upgradeprovision b/source4/scripting/bin/upgradeprovision index 400d629602..c22f3ce43b 100755 --- a/source4/scripting/bin/upgradeprovision +++ b/source4/scripting/bin/upgradeprovision @@ -47,7 +47,7 @@ from samba.provision import (find_setup_dir, get_domain_descriptor,                              ProvisioningError, get_last_provision_usn,                              get_max_usn, update_provision_usn)  from samba.schema import get_linked_attributes, Schema, get_schema_descriptor -from samba.dcerpc import security, drsblobs +from samba.dcerpc import security, drsblobs, xattr  from samba.ndr import ndr_unpack  from samba.upgradehelpers import (dn_sort, get_paths, newprovision,                                   find_provision_key_parameters, get_ldbs, @@ -1002,6 +1002,7 @@ def check_updated_sd(ref_sam, cur_sam, names):                      str(reference[i]["nTSecurityDescriptor"]))          hash[str(reference[i]["dn"]).lower()] = refsd.as_sddl(names.domainsid) +      for i in range(0, len(current)):          key = str(current[i]["dn"]).lower()          if hash.has_key(key): @@ -1113,6 +1114,18 @@ def removeProvisionUSN(samdb):          delta.dn = entry[0].dn          samdb.modify(delta) +def remove_stored_generated_attrs(paths, creds, session, lp): +    """Remove previously stored constructed attributes + +    :param paths: List of paths for different provision objects +                        from the upgraded provision +    :param creds: A credential object +    :param session: A session object +    :param lp: A line parser object +    :return: An associative array whose key are the different constructed +             attributes and the value the dn where this attributes were found. +     """ +  def simple_update_basesamdb(newpaths, paths, names):      """Update the provision container db: sam.ldb @@ -1185,6 +1198,96 @@ def update_samdb(ref_samdb, samdb, names, highestUSN, schema):          return 0 +def copyxattrs(dir, refdir): +    """ Copy owner, groups, extended ACL and NT acls from +    a reference dir to a destination dir + +    Both dir are supposed to hold the same files +    :param dir: Destination dir +    :param refdir: Reference directory""" + +    noxattr = 0 +    for root, dirs, files in os.walk(dir, topdown=True): +        for name in files: +            subdir=root[len(dir):] +            ref = os.path.join("%s%s" % (refdir, subdir), name) +            statsinfo = os.stat(ref) +            tgt = os.path.join(root, name) +            try: + +                os.chown(tgt, statsinfo.st_uid, statsinfo.st_gid) +                # Get the xattr attributes if any +                try: +                    attribute = samba.xattr_native.wrap_getxattr(ref, +                                                 xattr.XATTR_NTACL_NAME) +                    samba.xattr_native.wrap_setxattr(tgt, +                                                 xattr.XATTR_NTACL_NAME, +                                                 attribute) +                except: +                    noxattr = 1 +                attribute = samba.xattr_native.wrap_getxattr(ref, +                                                 "system.posix_acl_access") +                samba.xattr_native.wrap_setxattr(tgt, +                                                 "system.posix_acl_access", +                                                  attribute) +            except: +                continue +        for name in dirs: +            subdir=root[len(dir):] +            ref = os.path.join("%s%s" % (refdir, subdir), name) +            statsinfo = os.stat(ref) +            tgt = os.path.join(root, name) +            try: +                os.chown(os.path.join(root, name), statsinfo.st_uid, +                          statsinfo.st_gid) +                try: +                    attribute = samba.xattr_native.wrap_getxattr(ref, +                                                 xattr.XATTR_NTACL_NAME) +                    samba.xattr_native.wrap_setxattr(tgt, +                                                 xattr.XATTR_NTACL_NAME, +                                                 attribute) +                except: +                    noxattr = 1 +                attribute = samba.xattr_native.wrap_getxattr(ref, +                                                 "system.posix_acl_access") +                samba.xattr_native.wrap_setxattr(tgt, +                                                 "system.posix_acl_access", +                                                  attribute) + +            except: +                continue + + +def backup_provision(paths, dir): +    """This function backup the provision files so that a rollback +    is possible + +    :param paths: Paths to different objects +    :param dir: Directory where to store the backup +    """ + +    shutil.copytree(paths.sysvol, os.path.join(dir, "sysvol")) +    copyxattrs(os.path.join(dir, "sysvol"), paths.sysvol) +    shutil.copy2(paths.samdb, dir) +    shutil.copy2(paths.secrets, dir) +    shutil.copy2(paths.idmapdb, dir) +    shutil.copy2(paths.privilege, dir) +    if os.path.isfile(os.path.join(paths.private_dir,"eadb.tdb")): +        shutil.copy2(os.path.join(paths.private_dir,"eadb.tdb"), dir) +    shutil.copy2(paths.smbconf, dir) +    shutil.copy2(os.path.join(paths.private_dir,"secrets.keytab"), dir) + +    samldbdir = os.path.join(paths.private_dir, "sam.ldb.d") +    if not os.path.isdir(samldbdir): +        samldbdir = paths.private_dir +        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) +    else: +        shutil.copytree(samldbdir, os.path.join(dir, "sam.ldb.d"))  def setup_path(file):      return os.path.join(setup_dir, file) @@ -1210,12 +1313,13 @@ def setup_path(file):  #    A) When alpha9 or alphaxx is present  #       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. The highest used USN -#       is fetched so that changed by upgradeprovision usn can be tracked +#       exception of lastProvisionUSN attributes.  #    B) Other case (it reflect that that provision was done before alpha9)  #       The base sam.ldb of the reference provision is copied over  #       the current one, if necessary ldb related to partitions are moved  #       and renamed +# The highest used USN is fetched so that changed by upgradeprovision +# usn can be tracked  # 12)A Schema object is created, it will be used to provide a complete  #    schema to current provision during update (as the schema of the  #    current provision might not be complete and so won't allow some @@ -1338,158 +1442,170 @@ if __name__ == '__main__':      minUSN = 0      # 2)      ldbs = get_ldbs(paths, creds, session, lp) -    ldbs.startTransactions() - -    # 3) Guess all the needed names (variables in fact) from the current -    # provision. -    names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, -                                            paths, smbconf, lp) -    # 4) -    lastProvisionUSNs = get_last_provision_usn(ldbs.sam) -    if lastProvisionUSNs is not None: -        message(CHANGE, -            "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) - -    # Objects will be created with the admin session -    # (not anymore system session) -    adm_session = admin_session(lp, str(names.domainsid)) -    # So we reget handle on objects -    # ldbs = get_ldbs(paths, creds, adm_session, lp) - -    if not sanitychecks(ldbs.sam, names): -        message(SIMPLE, "Sanity checks for the upgrade fails, checks messages"  -                        " and correct them before rerunning upgradeprovision") -        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") -    newprovision(names, setup_dir, creds, session, smbconf, provisiondir, -                    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() - -    # 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() -    # 11) -    if 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 +    backupdir = tempfile.mkdtemp(dir=paths.private_dir, +                                    prefix="backupprovision") +    backup_provision(paths, backupdir) +    try: +        ldbs.startTransactions() + +        # 3) Guess all the needed names (variables in fact) from the current +        # provision. +        names = find_provision_key_parameters(ldbs.sam, ldbs.secrets, ldbs.idmap, +                                                paths, smbconf, lp) +        # 4) +        lastProvisionUSNs = get_last_provision_usn(ldbs.sam) +        if lastProvisionUSNs is not None: +            message(CHANGE, +                "Find a last provision USN, %d range(s)" % len(lastProvisionUSNs)) + +        # Objects will be created with the admin session +        # (not anymore system session) +        adm_session = admin_session(lp, str(names.domainsid)) +        # So we reget handle on objects +        # ldbs = get_ldbs(paths, creds, adm_session, lp) + +        if not sanitychecks(ldbs.sam, names): +            message(SIMPLE, "Sanity checks for the upgrade fails, checks messages" +                            " and correct them before rerunning upgradeprovision") +            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") +        newprovision(names, setup_dir, creds, session, smbconf, provisiondir, +                        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() + +        # 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() -        delta_update_basesamdb(newpaths.samdb, paths.samdb, creds, session, lp, message) + +        # 11) +        if 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 +            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() -    else: -        # 11) B -        simple_update_basesamdb(newpaths, paths, names) -        ldbs = get_ldbs(paths, creds, session, lp) -        removeProvisionUSN(ldbs.sam) -        ldbs.startTransactions() -    # 12) -    schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn), -                     serverdn=str(names.serverdn)) -    # 13) -    if opts.full: -        if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, -                            schema): -            message(SIMPLE, "Rollbacking every changes. Check the reason" -                            " of the problem") -            message(SIMPLE, "In any case your system as it was before" -                            " the upgrade") -            ldbs.groupedRollback() -            new_ldbs.groupedRollback() -            shutil.rmtree(provisiondir) -            sys.exit(1) -    # 14) -    update_secrets(new_ldbs.secrets, ldbs.secrets, message) -    # 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 not re.match(r'.*alpha(9|\d\d+)', str(oem)): -        message(SIMPLE, "Fixing old povision SD") -        fix_partition_sd(ldbs.sam, names) -        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 only if defaultSecurityDescriptor is modified -    # But in fact we should do it also if one object has its SD modified as -    # child might need rebuild -    if defSDmodified: -        message(SIMPLE, "Updating SD") -        ldbs.sam.set_session_info(adm_session) -        # Alpha10 was a bit broken still -        if re.match(r'.*alpha(\d|10)', str(oem)): +        # 12) +        schema = Schema(setup_path, names.domainsid, schemadn=str(names.schemadn), +                         serverdn=str(names.serverdn)) + +        # 13) +        if opts.full: +            if not update_samdb(new_ldbs.sam, ldbs.sam, names, lastProvisionUSNs, +                                schema): +                message(SIMPLE, "Rollbacking every changes. Check the reason" +                                " of the problem") +                message(SIMPLE, "In any case your system as it was before" +                                " the upgrade") +                ldbs.groupedRollback() +                new_ldbs.groupedRollback() +                shutil.rmtree(provisiondir) +                sys.exit(1) +        # 14) +        update_secrets(new_ldbs.secrets, ldbs.secrets, message) +        # 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 not re.match(r'.*alpha(9|\d\d+)', str(oem)): +            message(SIMPLE, "Fixing old povision SD")              fix_partition_sd(ldbs.sam, names) -        rebuild_sd(ldbs.sam, names) - -    # 19) -    # Now we are quite confident in the recalculate process of the SD, we make -    # it optional. -    # Also the check must be done in a clever way as for the moment we just -    # compare SDDL -    if opts.debugchangesd: -        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) -    if opts.full and (names.policyid is None or names.policyid_dc is None): -        update_policyids(names, ldbs.sam) -    if opts.full or opts.resetfileacl: -        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") -    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") -    ldbs.groupedCommit() -    new_ldbs.groupedCommit() -    message(SIMPLE, "Upgrade finished !") -    # remove reference provision now that everything is done ! -    shutil.rmtree(provisiondir) +            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 only if defaultSecurityDescriptor is modified +        # But in fact we should do it also if one object has its SD modified as +        # child might need rebuild +        if defSDmodified: +            message(SIMPLE, "Updating SD") +            ldbs.sam.set_session_info(adm_session) +            # Alpha10 was a bit broken still +            if re.match(r'.*alpha(\d|10)', str(oem)): +                fix_partition_sd(ldbs.sam, names) +            rebuild_sd(ldbs.sam, names) + +        # 19) +        # Now we are quite confident in the recalculate process of the SD, we make +        # it optional. +        # Also the check must be done in a clever way as for the moment we just +        # compare SDDL +        if opts.debugchangesd: +            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) +        if opts.full and (names.policyid is None or names.policyid_dc is None): +            update_policyids(names, ldbs.sam) +        if opts.full or opts.resetfileacl: +            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") +        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") +        ldbs.groupedCommit() +        new_ldbs.groupedCommit() +        message(SIMPLE, "Upgrade finished !") +        # remove reference provision now that everything is done ! +        shutil.rmtree(provisiondir) +    except StandardError, err: +        message(ERROR,"A problem has occured when trying to upgrade your provision," +                      " a full backup is located at %s" % backupdir) +        if opts.changeall: +            (typ, val, tb) = sys.exc_info() +            traceback.print_exception(typ, val, tb)  | 
