Beispiel #1
0
class CredentialsOptionsDouble(CredentialsOptions):
    """Command line options for specifying credentials of two servers."""
    def __init__(self, parser):
        CredentialsOptions.__init__(self, parser)
        self.no_pass2 = True
        self.add_option("--simple-bind-dn2", metavar="DN2", action="callback",
                        callback=self._set_simple_bind_dn2, type=str,
                        help="DN to use for a simple bind")
        self.add_option("--password2", metavar="PASSWORD2", action="callback",
                        help="Password", type=str,
                        callback=self._set_password2)
        self.add_option("--username2", metavar="USERNAME2",
                        action="callback", type=str,
                        help="Username for second server",
                        callback=self._parse_username2)
        self.add_option("--workgroup2", metavar="WORKGROUP2",
                        action="callback", type=str,
                        help="Workgroup for second server",
                        callback=self._parse_workgroup2)
        self.add_option("--no-pass2", action="store_true",
                        help="Don't ask for a password for the second server")
        self.add_option("--kerberos2", metavar="KERBEROS2",
                        action="callback", type=str,
                        help="Use Kerberos", callback=self._set_kerberos2)
        self.creds2 = Credentials()

    def _parse_username2(self, option, opt_str, arg, parser):
        self.creds2.parse_string(arg)

    def _parse_workgroup2(self, option, opt_str, arg, parser):
        self.creds2.set_domain(arg)

    def _set_password2(self, option, opt_str, arg, parser):
        self.creds2.set_password(arg)
        self.no_pass2 = False

    def _set_kerberos2(self, option, opt_str, arg, parser):
        if bool(arg) or arg.lower() == "yes":
            self.creds2.set_kerberos_state(MUST_USE_KERBEROS)
        else:
            self.creds2.set_kerberos_state(DONT_USE_KERBEROS)

    def _set_simple_bind_dn2(self, option, opt_str, arg, parser):
        self.creds2.set_bind_dn(arg)

    def get_credentials2(self, lp, guess=True):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :param guess: Try guess Credentials from environment
        :return: Credentials object
        """
        if guess:
            self.creds2.guess(lp)
        elif not self.creds2.get_username():
                self.creds2.set_anonymous()

        if self.no_pass2:
            self.creds2.set_cmdline_callbacks()
        return self.creds2
Beispiel #2
0
class CredentialsOptionsDouble(CredentialsOptions):
    """Command line options for specifying credentials of two servers."""

    def __init__(self, parser):
        CredentialsOptions.__init__(self, parser)
        self.no_pass2 = True
        self.add_option("--simple-bind-dn2", metavar="DN2", action="callback",
                        callback=self._set_simple_bind_dn2, type=str,
                        help="DN to use for a simple bind")
        self.add_option("--password2", metavar="PASSWORD2", action="callback",
                        help="Password", type=str,
                        callback=self._set_password2)
        self.add_option("--username2", metavar="USERNAME2",
                        action="callback", type=str,
                        help="Username for second server",
                        callback=self._parse_username2)
        self.add_option("--workgroup2", metavar="WORKGROUP2",
                        action="callback", type=str,
                        help="Workgroup for second server",
                        callback=self._parse_workgroup2)
        self.add_option("--no-pass2", action="store_true",
                        help="Don't ask for a password for the second server")
        self.add_option("--kerberos2", metavar="KERBEROS2",
                        action="callback", type=str,
                        help="Use Kerberos", callback=self._set_kerberos2)
        self.creds2 = Credentials()

    def _parse_username2(self, option, opt_str, arg, parser):
        self.creds2.parse_string(arg)

    def _parse_workgroup2(self, option, opt_str, arg, parser):
        self.creds2.set_domain(arg)

    def _set_password2(self, option, opt_str, arg, parser):
        self.creds2.set_password(arg)
        self.no_pass2 = False

    def _set_kerberos2(self, option, opt_str, arg, parser):
        self.creds2.set_kerberos_state(parse_kerberos_arg(arg, opt_str))

    def _set_simple_bind_dn2(self, option, opt_str, arg, parser):
        self.creds2.set_bind_dn(arg)

    def get_credentials2(self, lp, guess=True):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :param guess: Try guess Credentials from environment
        :return: Credentials object
        """
        if guess:
            self.creds2.guess(lp)
        elif not self.creds2.get_username():
            self.creds2.set_anonymous()

        if self.no_pass2:
            self.creds2.set_cmdline_callbacks()
        return self.creds2
Beispiel #3
0
    common_sids = group_sids.intersection(user_sids)
    if common_sids:
        logger.error("Following sids are both user and group sids:")
        for sid in common_sids:
            logger.error("   %s" % str(sid))
        raise ProvisioningError(
            "Please remove duplicate sid entries before upgrade.")

    # Get posix attributes from ldap or the os
    homes = {}
    shells = {}
    pgids = {}
    if ldap:
        creds = Credentials()
        creds.guess(samba3.lp)
        creds.set_bind_dn(ldapuser)
        creds.set_password(ldappass)
        urls = samba3.lp.get("passdb backend").split(":", 1)[1].strip('"')
        for url in urls.split():
            try:
                ldb_object = Ldb(url, credentials=creds)
            except ldb.LdbError, e:
                logger.warning(
                    "Could not open ldb connection to %s, the error message is: %s",
                    url, e)
            else:
                break
    logger.info("Exporting posix attributes")
    userlist = s3db.search_users(0)
    for entry in userlist:
        username = entry['account_name']
Beispiel #4
0
        raise ProvisioningError("Please remove duplicate user sid entries before upgrade.")
    common_sids = group_sids.intersection(user_sids)
    if common_sids:
        logger.error("Following sids are both user and group sids:")
        for sid in common_sids:
            logger.error("   %s" % str(sid))
        raise ProvisioningError("Please remove duplicate sid entries before upgrade.")

    # Get posix attributes from ldap or the os
    homes = {}
    shells = {}
    pgids = {}
    if ldap:
        creds = Credentials()
        creds.guess(samba3.lp)
        creds.set_bind_dn(ldapuser)
        creds.set_password(ldappass)
        urls = samba3.lp.get("passdb backend").split(":",1)[1].strip('"')
        for url in urls.split():
            try:
                ldb_object = Ldb(url, credentials=creds)
            except ldb.LdbError, e:
                raise ProvisioningError("Could not open ldb connection to %s, the error message is: %s" % (url, e))
            else:
                break
    logger.info("Exporting posix attributes")
    userlist = s3db.search_users(0)
    for entry in userlist:
        username = entry['account_name']
        if username in uids.keys():
            try:
Beispiel #5
0
class CredentialsOptions(optparse.OptionGroup):
    """Command line options for specifying credentials."""
    def __init__(self, parser):
        self.no_pass = True
        self.ipaddress = None
        optparse.OptionGroup.__init__(self, parser, "Credentials Options")
        self.add_option("--simple-bind-dn",
                        metavar="DN",
                        action="callback",
                        callback=self._set_simple_bind_dn,
                        type=str,
                        help="DN to use for a simple bind")
        self.add_option("--password",
                        metavar="PASSWORD",
                        action="callback",
                        help="Password",
                        type=str,
                        callback=self._set_password)
        self.add_option("-U",
                        "--username",
                        metavar="USERNAME",
                        action="callback",
                        type=str,
                        help="Username",
                        callback=self._parse_username)
        self.add_option("-W",
                        "--workgroup",
                        metavar="WORKGROUP",
                        action="callback",
                        type=str,
                        help="Workgroup",
                        callback=self._parse_workgroup)
        self.add_option("-N",
                        "--no-pass",
                        action="store_true",
                        help="Don't ask for a password")
        self.add_option("-k",
                        "--kerberos",
                        metavar="KERBEROS",
                        action="callback",
                        type=str,
                        help="Use Kerberos",
                        callback=self._set_kerberos)
        self.add_option("",
                        "--ipaddress",
                        metavar="IPADDRESS",
                        action="callback",
                        type=str,
                        help="IP address of server",
                        callback=self._set_ipaddress)
        self.creds = Credentials()

    def _parse_username(self, option, opt_str, arg, parser):
        self.creds.parse_string(arg)

    def _parse_workgroup(self, option, opt_str, arg, parser):
        self.creds.set_domain(arg)

    def _set_password(self, option, opt_str, arg, parser):
        self.creds.set_password(arg)
        self.no_pass = False

    def _set_ipaddress(self, option, opt_str, arg, parser):
        self.ipaddress = arg

    def _set_kerberos(self, option, opt_str, arg, parser):
        self.creds.set_kerberos_state(parse_kerberos_arg(arg, opt_str))

    def _set_simple_bind_dn(self, option, opt_str, arg, parser):
        self.creds.set_bind_dn(arg)

    def get_credentials(self, lp, fallback_machine=False):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :return: Credentials object
        """
        self.creds.guess(lp)
        if self.no_pass:
            self.creds.set_cmdline_callbacks()

        # possibly fallback to using the machine account, if we have
        # access to the secrets db
        if fallback_machine and not self.creds.authentication_requested():
            try:
                self.creds.set_machine_account(lp)
            except Exception:
                pass

        return self.creds
Beispiel #6
0
def upgrade_from_samba3(samba3,
                        logger,
                        targetdir,
                        session_info=None,
                        useeadb=False,
                        dns_backend=None,
                        use_ntvfs=False):
    """Upgrade from samba3 database to samba4 AD database

    :param samba3: samba3 object
    :param logger: Logger object
    :param targetdir: samba4 database directory
    :param session_info: Session information
    """
    serverrole = samba3.lp.server_role()

    domainname = samba3.lp.get("workgroup")
    realm = samba3.lp.get("realm")
    netbiosname = samba3.lp.get("netbios name")

    if samba3.lp.get("ldapsam:trusted") is None:
        samba3.lp.set("ldapsam:trusted", "yes")

    # secrets db
    try:
        secrets_db = samba3.get_secrets_db()
    except IOError as e:
        raise ProvisioningError(
            "Could not open '%s', the Samba3 secrets database: %s.  Perhaps you specified the incorrect smb.conf, --testparm or --dbdir option?"
            % (samba3.privatedir_path("secrets.tdb"), str(e)))

    if not domainname:
        domainname = secrets_db.domains()[0]
        logger.warning(
            "No workgroup specified in smb.conf file, assuming '%s'",
            domainname)

    if not realm:
        if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
            raise ProvisioningError(
                "No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (it is the DNS name of the AD domain you wish to create."
            )
        else:
            realm = domainname.upper()
            logger.warning(
                "No realm specified in smb.conf file, assuming '%s'", realm)

    # Find machine account and password
    next_rid = 1000

    try:
        machinepass = secrets_db.get_machine_password(netbiosname)
    except KeyError:
        machinepass = None

    if samba3.lp.get("passdb backend").split(":")[0].strip() == "ldapsam":
        base_dn = samba3.lp.get("ldap suffix")
        ldapuser = samba3.lp.get("ldap admin dn")
        ldappass = secrets_db.get_ldap_bind_pw(ldapuser)
        if ldappass is None:
            raise ProvisioningError(
                "ldapsam passdb backend detected but no LDAP Bind PW found in secrets.tdb for user %s.  Please point this tool at the secrets.tdb that was used by the previous installation."
            )
        ldappass = ldappass.decode('utf-8').strip('\x00')
        ldap = True
    else:
        ldapuser = None
        ldappass = None
        ldap = False

    # We must close the direct pytdb database before the C code loads it
    secrets_db.close()

    # Connect to old password backend
    passdb.set_secrets_dir(samba3.lp.get("private dir"))
    s3db = samba3.get_sam_db()

    # Get domain sid
    try:
        domainsid = passdb.get_global_sam_sid()
    except passdb.error:
        raise Exception("Can't find domain sid for '%s', Exiting." %
                        domainname)

    # Get machine account, sid, rid
    try:
        machineacct = s3db.getsampwnam('%s$' % netbiosname)
    except passdb.error:
        machinerid = None
        machinesid = None
    else:
        machinesid, machinerid = machineacct.user_sid.split()

    # Export account policy
    logger.info("Exporting account policy")
    policy = s3db.get_account_policy()

    # Export groups from old passdb backend
    logger.info("Exporting groups")
    grouplist = s3db.enum_group_mapping()
    groupmembers = {}
    for group in grouplist:
        sid, rid = group.sid.split()
        if sid == domainsid:
            if rid >= next_rid:
                next_rid = rid + 1

        # Get members for each group/alias
        if group.sid_name_use == lsa.SID_NAME_ALIAS:
            try:
                members = s3db.enum_aliasmem(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn(
                    "Ignoring group '%s' %s listed but then not found: %s",
                    group.nt_name, group.sid, e)
                continue
        elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
            try:
                members = s3db.enum_group_members(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn(
                    "Ignoring group '%s' %s listed but then not found: %s",
                    group.nt_name, group.sid, e)
                continue
        elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
            (group_dom_sid, rid) = group.sid.split()
            if (group_dom_sid != security.dom_sid(security.SID_BUILTIN)):
                logger.warn(
                    "Ignoring 'well known' group '%s' (should already be in AD, and have no members)",
                    group.nt_name)
                continue
            # A number of buggy databases mix up well known groups and aliases.
            try:
                members = s3db.enum_aliasmem(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn(
                    "Ignoring group '%s' %s listed but then not found: %s",
                    group.nt_name, group.sid, e)
                continue
        else:
            logger.warn("Ignoring group '%s' %s with sid_name_use=%d",
                        group.nt_name, group.sid, group.sid_name_use)
            continue

    # Export users from old passdb backend
    logger.info("Exporting users")
    userlist = s3db.search_users(0)
    userdata = {}
    uids = {}
    admin_user = None
    for entry in userlist:
        if machinerid and machinerid == entry['rid']:
            continue
        username = entry['account_name']
        if entry['rid'] < 1000:
            logger.info("  Skipping wellknown rid=%d (for username=%s)",
                        entry['rid'], username)
            continue
        if entry['rid'] >= next_rid:
            next_rid = entry['rid'] + 1

        user = s3db.getsampwnam(username)
        acct_type = (user.acct_ctrl &
                     (samr.ACB_NORMAL | samr.ACB_WSTRUST | samr.ACB_SVRTRUST
                      | samr.ACB_DOMTRUST))
        if acct_type == samr.ACB_SVRTRUST:
            logger.warn(
                "  Demoting BDC account trust for %s, this DC must be elevated to an AD DC using 'samba-tool domain dcpromo'"
                % username[:-1])
            user.acct_ctrl = (user.acct_ctrl
                              & ~samr.ACB_SVRTRUST) | samr.ACB_WSTRUST

        elif acct_type == samr.ACB_DOMTRUST:
            logger.warn(
                "  Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust"
                % username[:-1])
            continue

        elif acct_type == (samr.ACB_WSTRUST) and username[-1] != '$':
            logger.warn(
                "  Skipping account %s that has ACB_WSTRUST (W) set but does not end in $.  This account can not have worked, and is probably left over from a misconfiguration."
                % username)
            continue

        elif acct_type == (samr.ACB_NORMAL
                           | samr.ACB_WSTRUST) and username[-1] == '$':
            logger.warn(
                "  Fixing account %s which had both ACB_NORMAL (U) and ACB_WSTRUST (W) set.  Account will be marked as ACB_WSTRUST (W), i.e. as a domain member"
                % username)
            user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_NORMAL)

        elif acct_type == (samr.ACB_NORMAL
                           | samr.ACB_SVRTRUST) and username[-1] == '$':
            logger.warn(
                "  Fixing account %s which had both ACB_NORMAL (U) and ACB_SVRTRUST (S) set.  Account will be marked as ACB_WSTRUST (S), i.e. as a domain member"
                % username)
            user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_NORMAL)

        elif acct_type == 0 and username[-1] != '$':
            user.acct_ctrl = (user.acct_ctrl | samr.ACB_NORMAL)

        elif (acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST):
            pass

        else:
            raise ProvisioningError(
                """Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of
ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X).

Please fix this account before attempting to upgrade again
""" % (username, user.acct_ctrl, samr.ACB_NORMAL, samr.ACB_WSTRUST,
            samr.ACB_SVRTRUST, samr.ACB_DOMTRUST))

        userdata[username] = user
        try:
            uids[username] = s3db.sid_to_id(user.user_sid)[0]
        except passdb.error:
            try:
                uids[username] = pwd.getpwnam(username).pw_uid
            except KeyError:
                pass

        if not admin_user and username.lower() == 'root':
            admin_user = username
        if username.lower() == 'administrator':
            admin_user = username

        try:
            group_memberships = s3db.enum_group_memberships(user)
            for group in group_memberships:
                if str(group) in groupmembers:
                    if user.user_sid not in groupmembers[str(group)]:
                        groupmembers[str(group)].append(user.user_sid)
                else:
                    groupmembers[str(group)] = [user.user_sid]
        except passdb.error as e:
            logger.warn("Ignoring group memberships of '%s' %s: %s", username,
                        user.user_sid, e)

    logger.info("Next rid = %d", next_rid)

    # Check for same username/groupname
    group_names = set([g.nt_name for g in grouplist])
    user_names = set([u['account_name'] for u in userlist])
    common_names = group_names.intersection(user_names)
    if common_names:
        logger.error("Following names are both user names and group names:")
        for name in common_names:
            logger.error("   %s" % name)
        raise ProvisioningError(
            "Please remove common user/group names before upgrade.")

    # Check for same user sid/group sid
    group_sids = set([str(g.sid) for g in grouplist])
    if len(grouplist) != len(group_sids):
        raise ProvisioningError(
            "Please remove duplicate group sid entries before upgrade.")
    user_sids = set(["%s-%u" % (domainsid, u['rid']) for u in userlist])
    if len(userlist) != len(user_sids):
        raise ProvisioningError(
            "Please remove duplicate user sid entries before upgrade.")
    common_sids = group_sids.intersection(user_sids)
    if common_sids:
        logger.error("Following sids are both user and group sids:")
        for sid in common_sids:
            logger.error("   %s" % str(sid))
        raise ProvisioningError(
            "Please remove duplicate sid entries before upgrade.")

    # Get posix attributes from ldap or the os
    homes = {}
    shells = {}
    pgids = {}
    if ldap:
        creds = Credentials()
        creds.guess(samba3.lp)
        creds.set_bind_dn(ldapuser)
        creds.set_password(ldappass)
        urls = samba3.lp.get("passdb backend").split(":", 1)[1].strip('"')
        for url in urls.split():
            try:
                ldb_object = Ldb(url, credentials=creds)
            except ldb.LdbError as e:
                raise ProvisioningError(
                    "Could not open ldb connection to %s, the error message is: %s"
                    % (url, e))
            else:
                break
    logger.info("Exporting posix attributes")
    userlist = s3db.search_users(0)
    for entry in userlist:
        username = entry['account_name']
        if username in uids.keys():
            try:
                if ldap:
                    homes[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "homeDirectory")
                else:
                    homes[username] = pwd.getpwnam(username).pw_dir
            except KeyError:
                pass
            except IndexError:
                pass

            try:
                if ldap:
                    shells[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "loginShell")
                else:
                    shells[username] = pwd.getpwnam(username).pw_shell
            except KeyError:
                pass
            except IndexError:
                pass

            try:
                if ldap:
                    pgids[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "gidNumber")
                else:
                    pgids[username] = pwd.getpwnam(username).pw_gid
            except KeyError:
                pass
            except IndexError:
                pass

    logger.info("Reading WINS database")
    samba3_winsdb = None
    try:
        samba3_winsdb = samba3.get_wins_db()
    except IOError as e:
        logger.warn('Cannot open wins database, Ignoring: %s', str(e))

    if not (serverrole == "ROLE_DOMAIN_BDC"
            or serverrole == "ROLE_DOMAIN_PDC"):
        dns_backend = "NONE"

    # If we found an admin user, set a fake pw that we will override.
    # This avoids us printing out an admin password that we won't actually
    # set.
    if admin_user:
        adminpass = generate_random_password(12, 32)
    else:
        adminpass = None

    # Do full provision
    result = provision(logger,
                       session_info,
                       targetdir=targetdir,
                       realm=realm,
                       domain=domainname,
                       domainsid=domainsid,
                       next_rid=next_rid,
                       dc_rid=machinerid,
                       adminpass=adminpass,
                       dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
                       hostname=netbiosname.lower(),
                       machinepass=machinepass,
                       serverrole=serverrole,
                       samdb_fill=FILL_FULL,
                       useeadb=useeadb,
                       dns_backend=dns_backend,
                       use_rfc2307=True,
                       use_ntvfs=use_ntvfs,
                       skip_sysvolacl=True)
    result.report_logger(logger)

    # Import WINS database
    logger.info("Importing WINS database")

    if samba3_winsdb:
        import_wins(Ldb(result.paths.winsdb), samba3_winsdb)

    # Set Account policy
    logger.info("Importing Account policy")
    import_sam_policy(result.samdb, policy, logger)

    # Migrate IDMAP database
    logger.info("Importing idmap database")
    import_idmap(result.idmap, samba3, logger)

    # Set the s3 context for samba4 configuration
    new_lp_ctx = s3param.get_context()
    new_lp_ctx.load(result.lp.configfile)
    new_lp_ctx.set("private dir", result.lp.get("private dir"))
    new_lp_ctx.set("state directory", result.lp.get("state directory"))
    new_lp_ctx.set("lock directory", result.lp.get("lock directory"))

    # Connect to samba4 backend
    s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))

    # Start a new transaction (should speed this up a little, due to index churn)
    result.samdb.transaction_start()

    logger.info("Adding groups")
    try:
        # Export groups to samba4 backend
        logger.info("Importing groups")
        for g in grouplist:
            # Ignore uninitialized groups (gid = -1)
            if g.gid != -1:
                add_group_from_mapping_entry(result.samdb, g, logger)
                add_ad_posix_idmap_entry(result.samdb, g.sid, g.gid,
                                         "ID_TYPE_GID", logger)
                add_posix_attrs(samdb=result.samdb,
                                sid=g.sid,
                                name=g.nt_name,
                                nisdomain=domainname.lower(),
                                xid_type="ID_TYPE_GID",
                                logger=logger)

    except:
        # We need this, so that we do not give even more errors due to not cancelling the transaction
        result.samdb.transaction_cancel()
        raise

    logger.info("Committing 'add groups' transaction to disk")
    result.samdb.transaction_commit()

    logger.info("Adding users")

    # Export users to samba4 backend
    logger.info("Importing users")
    for username in userdata:
        if username.lower() == 'administrator':
            if userdata[username].user_sid != dom_sid(str(domainsid) + "-500"):
                logger.error(
                    "User 'Administrator' in your existing directory has SID %s, expected it to be %s"
                    % (userdata[username].user_sid,
                       dom_sid(str(domainsid) + "-500")))
                raise ProvisioningError(
                    "User 'Administrator' in your existing directory does not have SID ending in -500"
                )
        if username.lower() == 'root':
            if userdata[username].user_sid == dom_sid(str(domainsid) + "-500"):
                logger.warn('User root has been replaced by Administrator')
            else:
                logger.warn(
                    'User root has been kept in the directory, it should be removed in favour of the Administrator user'
                )

        s4_passdb.add_sam_account(userdata[username])
        if username in uids:
            add_ad_posix_idmap_entry(result.samdb, userdata[username].user_sid,
                                     uids[username], "ID_TYPE_UID", logger)
            if (username in homes) and (homes[username] is not None) and \
               (username in shells) and (shells[username] is not None) and \
               (username in pgids) and (pgids[username] is not None):
                add_posix_attrs(samdb=result.samdb,
                                sid=userdata[username].user_sid,
                                name=username,
                                nisdomain=domainname.lower(),
                                xid_type="ID_TYPE_UID",
                                home=homes[username],
                                shell=shells[username],
                                pgid=pgids[username],
                                logger=logger)

    logger.info("Adding users to groups")
    # Start a new transaction (should speed this up a little, due to index churn)
    result.samdb.transaction_start()

    try:
        for g in grouplist:
            if str(g.sid) in groupmembers:
                add_users_to_group(result.samdb, g, groupmembers[str(g.sid)],
                                   logger)

    except:
        # We need this, so that we do not give even more errors due to not cancelling the transaction
        result.samdb.transaction_cancel()
        raise

    logger.info("Committing 'add users to groups' transaction to disk")
    result.samdb.transaction_commit()

    # Set password for administrator
    if admin_user:
        logger.info("Setting password for administrator")
        admin_userdata = s4_passdb.getsampwnam("administrator")
        admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
        if userdata[admin_user].lanman_passwd:
            admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
        admin_userdata.pass_last_set_time = userdata[
            admin_user].pass_last_set_time
        if userdata[admin_user].pw_history:
            admin_userdata.pw_history = userdata[admin_user].pw_history
        s4_passdb.update_sam_account(admin_userdata)
        logger.info(
            "Administrator password has been set to password of user '%s'",
            admin_user)

    if result.server_role == "active directory domain controller":
        setsysvolacl(result.samdb, result.paths.netlogon, result.paths.sysvol,
                     result.paths.root_uid, result.paths.root_gid,
                     security.dom_sid(result.domainsid),
                     result.names.dnsdomain, result.names.domaindn, result.lp,
                     use_ntvfs)
Beispiel #7
0
class CredentialsOptions(optparse.OptionGroup):
    """Command line options for specifying credentials."""

    def __init__(self, parser, special_name=None):
        self.special_name = special_name
        if special_name is not None:
            self.section = "Credentials Options (%s)" % special_name
        else:
            self.section = "Credentials Options"

        self.ask_for_password = True
        self.ipaddress = None
        self.machine_pass = False
        optparse.OptionGroup.__init__(self, parser, self.section)
        self._add_option("--simple-bind-dn", metavar="DN", action="callback",
                        callback=self._set_simple_bind_dn, type=str,
                        help="DN to use for a simple bind")
        self._add_option("--password", metavar="PASSWORD", action="callback",
                        help="Password", type=str, callback=self._set_password)
        self._add_option("-U", "--username", metavar="USERNAME",
                        action="callback", type=str,
                        help="Username", callback=self._parse_username)
        self._add_option("-W", "--workgroup", metavar="WORKGROUP",
                        action="callback", type=str,
                        help="Workgroup", callback=self._parse_workgroup)
        self._add_option("-N", "--no-pass", action="callback",
                        help="Don't ask for a password",
                        callback=self._set_no_password)
        self._add_option("-k", "--kerberos", metavar="KERBEROS",
                        action="callback", type=str,
                        help="Use Kerberos", callback=self._set_kerberos)
        self._add_option("", "--ipaddress", metavar="IPADDRESS",
                        action="callback", type=str,
                        help="IP address of server",
                        callback=self._set_ipaddress)
        self._add_option("-P", "--machine-pass",
                        action="callback",
                        help="Use stored machine account password",
                        callback=self._set_machine_pass)
        self._add_option("--krb5-ccache", metavar="KRB5CCNAME",
                         action="callback", type=str,
                         help="Kerberos Credentials cache",
                         callback=self._set_krb5_ccache)
        self.creds = Credentials()

    def _add_option(self, *args1, **kwargs):
        if self.special_name is None:
            return self.add_option(*args1, **kwargs)

        args2 = ()
        for a in args1:
            if not a.startswith("--"):
                continue
            args2 += (a.replace("--", "--%s-" % self.special_name),)
        self.add_option(*args2, **kwargs)

    def _parse_username(self, option, opt_str, arg, parser):
        self.creds.parse_string(arg)
        self.machine_pass = False

    def _parse_workgroup(self, option, opt_str, arg, parser):
        self.creds.set_domain(arg)

    def _set_password(self, option, opt_str, arg, parser):
        self.creds.set_password(arg)
        self.ask_for_password = False
        self.machine_pass = False

    def _set_no_password(self, option, opt_str, arg, parser):
        self.ask_for_password = False

    def _set_machine_pass(self, option, opt_str, arg, parser):
        self.machine_pass = True

    def _set_ipaddress(self, option, opt_str, arg, parser):
        self.ipaddress = arg

    def _set_kerberos(self, option, opt_str, arg, parser):
        self.creds.set_kerberos_state(parse_kerberos_arg(arg, opt_str))

    def _set_simple_bind_dn(self, option, opt_str, arg, parser):
        self.creds.set_bind_dn(arg)

    def _set_krb5_ccache(self, option, opt_str, arg, parser):
        self.creds.set_named_ccache(arg)

    def get_credentials(self, lp, fallback_machine=False):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :return: Credentials object
        """
        self.creds.guess(lp)
        if self.machine_pass:
            self.creds.set_machine_account(lp)
        elif self.ask_for_password:
            self.creds.set_cmdline_callbacks()

        # possibly fallback to using the machine account, if we have
        # access to the secrets db
        if fallback_machine and not self.creds.authentication_requested():
            try:
                self.creds.set_machine_account(lp)
            except Exception:
                pass

        return self.creds
Beispiel #8
0
class CredentialsOptions(optparse.OptionGroup):
    """Command line options for specifying credentials."""
    def __init__(self, parser, special_name=None):
        self.special_name = special_name
        if special_name is not None:
            self.section = "Credentials Options (%s)" % special_name
        else:
            self.section = "Credentials Options"

        self.ask_for_password = True
        self.ipaddress = None
        self.machine_pass = False
        optparse.OptionGroup.__init__(self, parser, self.section)
        self._add_option("--simple-bind-dn",
                         metavar="DN",
                         action="callback",
                         callback=self._set_simple_bind_dn,
                         type=str,
                         help="DN to use for a simple bind")
        self._add_option("--password",
                         metavar="PASSWORD",
                         action="callback",
                         help="Password",
                         type=str,
                         callback=self._set_password)
        self._add_option("-U",
                         "--username",
                         metavar="USERNAME",
                         action="callback",
                         type=str,
                         help="Username",
                         callback=self._parse_username)
        self._add_option("-W",
                         "--workgroup",
                         metavar="WORKGROUP",
                         action="callback",
                         type=str,
                         help="Workgroup",
                         callback=self._parse_workgroup)
        self._add_option("-N",
                         "--no-pass",
                         action="callback",
                         help="Don't ask for a password",
                         callback=self._set_no_password)
        self._add_option("-k",
                         "--kerberos",
                         metavar="KERBEROS",
                         action="callback",
                         type=str,
                         help="Use Kerberos",
                         callback=self._set_kerberos)
        self._add_option("",
                         "--ipaddress",
                         metavar="IPADDRESS",
                         action="callback",
                         type=str,
                         help="IP address of server",
                         callback=self._set_ipaddress)
        self._add_option("-P",
                         "--machine-pass",
                         action="callback",
                         help="Use stored machine account password",
                         callback=self._set_machine_pass)
        self.creds = Credentials()

    def _add_option(self, *args1, **kwargs):
        if self.special_name is None:
            return self.add_option(*args1, **kwargs)

        args2 = ()
        for a in args1:
            if not a.startswith("--"):
                continue
            args2 += (a.replace("--", "--%s-" % self.special_name), )
        self.add_option(*args2, **kwargs)

    def _parse_username(self, option, opt_str, arg, parser):
        self.creds.parse_string(arg)
        self.machine_pass = False

    def _parse_workgroup(self, option, opt_str, arg, parser):
        self.creds.set_domain(arg)

    def _set_password(self, option, opt_str, arg, parser):
        self.creds.set_password(arg)
        self.ask_for_password = False
        self.machine_pass = False

    def _set_no_password(self, option, opt_str, arg, parser):
        self.ask_for_password = False

    def _set_machine_pass(self, option, opt_str, arg, parser):
        self.machine_pass = True

    def _set_ipaddress(self, option, opt_str, arg, parser):
        self.ipaddress = arg

    def _set_kerberos(self, option, opt_str, arg, parser):
        self.creds.set_kerberos_state(parse_kerberos_arg(arg, opt_str))

    def _set_simple_bind_dn(self, option, opt_str, arg, parser):
        self.creds.set_bind_dn(arg)

    def get_credentials(self, lp, fallback_machine=False):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :return: Credentials object
        """
        self.creds.guess(lp)
        if self.machine_pass:
            self.creds.set_machine_account(lp)
        elif self.ask_for_password:
            self.creds.set_cmdline_callbacks()

        # possibly fallback to using the machine account, if we have
        # access to the secrets db
        if fallback_machine and not self.creds.authentication_requested():
            try:
                self.creds.set_machine_account(lp)
            except Exception:
                pass

        return self.creds
Beispiel #9
0
class CredentialsOptions(optparse.OptionGroup):
    """Command line options for specifying credentials."""
    def __init__(self, parser):
        self.no_pass = True
        self.ipaddress = None
        optparse.OptionGroup.__init__(self, parser, "Credentials Options")
        self.add_option("--simple-bind-dn", metavar="DN", action="callback",
                        callback=self._set_simple_bind_dn, type=str,
                        help="DN to use for a simple bind")
        self.add_option("--password", metavar="PASSWORD", action="callback",
                        help="Password", type=str, callback=self._set_password)
        self.add_option("-U", "--username", metavar="USERNAME",
                        action="callback", type=str,
                        help="Username", callback=self._parse_username)
        self.add_option("-W", "--workgroup", metavar="WORKGROUP",
                        action="callback", type=str,
                        help="Workgroup", callback=self._parse_workgroup)
        self.add_option("-N", "--no-pass", action="store_true",
                        help="Don't ask for a password")
        self.add_option("-k", "--kerberos", metavar="KERBEROS",
                        action="callback", type=str,
                        help="Use Kerberos", callback=self._set_kerberos)
        self.add_option("", "--ipaddress", metavar="IPADDRESS",
                        action="callback", type=str,
                        help="IP address of server", callback=self._set_ipaddress)
        self.creds = Credentials()

    def _parse_username(self, option, opt_str, arg, parser):
        self.creds.parse_string(arg)

    def _parse_workgroup(self, option, opt_str, arg, parser):
        self.creds.set_domain(arg)

    def _set_password(self, option, opt_str, arg, parser):
        self.creds.set_password(arg)
        self.no_pass = False

    def _set_ipaddress(self, option, opt_str, arg, parser):
        self.ipaddress = arg

    def _set_kerberos(self, option, opt_str, arg, parser):
        if arg.lower() in ["yes", 'true', '1']:
            self.creds.set_kerberos_state(MUST_USE_KERBEROS)
        elif arg.lower() in ["no", 'false', '0']:
            self.creds.set_kerberos_state(DONT_USE_KERBEROS)
        else:
            raise optparse.BadOptionErr("invalid kerberos option: %s" % arg)

    def _set_simple_bind_dn(self, option, opt_str, arg, parser):
        self.creds.set_bind_dn(arg)

    def get_credentials(self, lp, fallback_machine=False):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :return: Credentials object
        """
        self.creds.guess(lp)
        if self.no_pass:
            self.creds.set_cmdline_callbacks()

        # possibly fallback to using the machine account, if we have
        # access to the secrets db
        if fallback_machine and not self.creds.authentication_requested():
            try:
                self.creds.set_machine_account(lp)
            except Exception:
                pass

        return self.creds
Beispiel #10
0
class CredentialsOptions(optparse.OptionGroup):
    """Command line options for specifying credentials."""

    def __init__(self, parser, special_name=None):
        self.special_name = special_name
        if special_name is not None:
            self.section = "Credentials Options (%s)" % special_name
        else:
            self.section = "Credentials Options"

        self.ask_for_password = True
        self.ipaddress = None
        self.machine_pass = False
        optparse.OptionGroup.__init__(self, parser, self.section)
        self._add_option("--simple-bind-dn", metavar="DN", action="callback",
                         callback=self._set_simple_bind_dn, type=str,
                         help="DN to use for a simple bind")
        self._add_option("--password", metavar="PASSWORD", action="callback",
                         help="Password", type=str, callback=self._set_password)
        self._add_option("-U", "--username", metavar="USERNAME",
                         action="callback", type=str,
                         help="Username", callback=self._parse_username)
        self._add_option("-W", "--workgroup", metavar="WORKGROUP",
                         action="callback", type=str,
                         help="Workgroup", callback=self._parse_workgroup)
        self._add_option("-N", "--no-pass", action="callback",
                         help="Don't ask for a password",
                         callback=self._set_no_password)
        self._add_option("-k", "--kerberos", metavar="KERBEROS",
                         action="callback", type=str,
                         help="Use Kerberos", callback=self._set_kerberos)
        self._add_option("", "--ipaddress", metavar="IPADDRESS",
                         action="callback", type=str,
                         help="IP address of server",
                         callback=self._set_ipaddress)
        self._add_option("-P", "--machine-pass",
                         action="callback",
                         help="Use stored machine account password",
                         callback=self._set_machine_pass)
        self._add_option("--krb5-ccache", metavar="KRB5CCNAME",
                         action="callback", type=str,
                         help="Kerberos Credentials cache",
                         callback=self._set_krb5_ccache)
        self.creds = Credentials()

    def _ensure_secure_proctitle(self, opt_str, secret_data, data_type="password"):
        """ Make sure no sensitive data (e.g. password) resides in proctitle. """
        import re
        try:
            import setproctitle
        except ModuleNotFoundError:
            msg = ("WARNING: Using %s on command line is insecure. "
                    "Please install the setproctitle python module.\n"
                    % data_type)
            sys.stderr.write(msg)
            sys.stderr.flush()
            return False
        # Regex to search and replace secret data + option with.
        #   .*[ ]+  -> Before the option must be one or more spaces.
        #   [= ]    -> The option and the secret data might be separated by space
        #              or equal sign.
        #   [ ]*.*  -> After the secret data might be one, many or no space.
        pass_opt_re_str = "(.*[ ]+)(%s[= ]%s)([ ]*.*)" % (opt_str, secret_data)
        pass_opt_re = re.compile(pass_opt_re_str)
        # Get current proctitle.
        cur_proctitle = setproctitle.getproctitle()
        # Make sure we build the correct regex.
        if not pass_opt_re.match(cur_proctitle):
            msg = ("Unable to hide %s in proctitle. This is most likely "
                    "a bug!\n" % data_type)
            sys.stderr.write(msg)
            sys.stderr.flush()
            return False
        # String to replace secret data with.
        secret_data_replacer = "xxx"
        # Build string to replace secret data and option with. And as we dont
        # want to change anything else than the secret data within the proctitle
        # we have to check if the option was passed with space or equal sign as
        # separator.
        opt_pass_with_eq = "%s=%s" % (opt_str, secret_data)
        opt_pass_part = re.sub(pass_opt_re_str, r'\2', cur_proctitle)
        if opt_pass_part == opt_pass_with_eq:
            replace_str = "%s=%s" % (opt_str, secret_data_replacer)
        else:
            replace_str = "%s %s" % (opt_str, secret_data_replacer)
        # Build new proctitle:
        new_proctitle = re.sub(pass_opt_re_str,
                            r'\1' + replace_str + r'\3',
                            cur_proctitle)
        # Set new proctitle.
        setproctitle.setproctitle(new_proctitle)

    def _add_option(self, *args1, **kwargs):
        if self.special_name is None:
            return self.add_option(*args1, **kwargs)

        args2 = ()
        for a in args1:
            if not a.startswith("--"):
                continue
            args2 += (a.replace("--", "--%s-" % self.special_name),)
        self.add_option(*args2, **kwargs)

    def _parse_username(self, option, opt_str, arg, parser):
        self.creds.parse_string(arg)
        self.machine_pass = False

    def _parse_workgroup(self, option, opt_str, arg, parser):
        self.creds.set_domain(arg)

    def _set_password(self, option, opt_str, arg, parser):
        self._ensure_secure_proctitle(opt_str, arg, "password")
        self.creds.set_password(arg)
        self.ask_for_password = False
        self.machine_pass = False

    def _set_no_password(self, option, opt_str, arg, parser):
        self.ask_for_password = False

    def _set_machine_pass(self, option, opt_str, arg, parser):
        self.machine_pass = True

    def _set_ipaddress(self, option, opt_str, arg, parser):
        self.ipaddress = arg

    def _set_kerberos(self, option, opt_str, arg, parser):
        self.creds.set_kerberos_state(parse_kerberos_arg(arg, opt_str))

    def _set_simple_bind_dn(self, option, opt_str, arg, parser):
        self.creds.set_bind_dn(arg)

    def _set_krb5_ccache(self, option, opt_str, arg, parser):
        self.creds.set_named_ccache(arg)

    def get_credentials(self, lp, fallback_machine=False):
        """Obtain the credentials set on the command-line.

        :param lp: Loadparm object to use.
        :return: Credentials object
        """
        self.creds.guess(lp)
        if self.machine_pass:
            self.creds.set_machine_account(lp)
        elif self.ask_for_password:
            self.creds.set_cmdline_callbacks()

        # possibly fallback to using the machine account, if we have
        # access to the secrets db
        if fallback_machine and not self.creds.authentication_requested():
            try:
                self.creds.set_machine_account(lp)
            except Exception:
                pass

        return self.creds
Beispiel #11
0
def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, useeadb=False, dns_backend=None, use_ntvfs=False):
    """Upgrade from samba3 database to samba4 AD database

    :param samba3: samba3 object
    :param logger: Logger object
    :param targetdir: samba4 database directory
    :param session_info: Session information
    """
    serverrole = samba3.lp.server_role()

    domainname = samba3.lp.get("workgroup")
    realm = samba3.lp.get("realm")
    netbiosname = samba3.lp.get("netbios name")

    if samba3.lp.get("ldapsam:trusted") is None:
        samba3.lp.set("ldapsam:trusted", "yes")

    # secrets db
    try:
        secrets_db = samba3.get_secrets_db()
    except IOError as e:
        raise ProvisioningError(
            "Could not open '%s', the Samba3 secrets database: %s.  Perhaps you specified the incorrect smb.conf, --testparm or --dbdir option?"
            % (samba3.privatedir_path("secrets.tdb"), str(e))
        )

    if not domainname:
        domainname = secrets_db.domains()[0]
        logger.warning("No workgroup specified in smb.conf file, assuming '%s'", domainname)

    if not realm:
        if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
            raise ProvisioningError(
                "No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (it is the DNS name of the AD domain you wish to create."
            )
        else:
            realm = domainname.upper()
            logger.warning("No realm specified in smb.conf file, assuming '%s'", realm)

    # Find machine account and password
    next_rid = 1000

    try:
        machinepass = secrets_db.get_machine_password(netbiosname)
    except KeyError:
        machinepass = None

    if samba3.lp.get("passdb backend").split(":")[0].strip() == "ldapsam":
        base_dn = samba3.lp.get("ldap suffix")
        ldapuser = samba3.lp.get("ldap admin dn")
        ldappass = secrets_db.get_ldap_bind_pw(ldapuser)
        if ldappass is None:
            raise ProvisioningError(
                "ldapsam passdb backend detected but no LDAP Bind PW found in secrets.tdb for user %s.  Please point this tool at the secrets.tdb that was used by the previous installation."
            )
        ldappass = ldappass.strip("\x00")
        ldap = True
    else:
        ldapuser = None
        ldappass = None
        ldap = False

    # We must close the direct pytdb database before the C code loads it
    secrets_db.close()

    # Connect to old password backend
    passdb.set_secrets_dir(samba3.lp.get("private dir"))
    s3db = samba3.get_sam_db()

    # Get domain sid
    try:
        domainsid = passdb.get_global_sam_sid()
    except passdb.error:
        raise Exception("Can't find domain sid for '%s', Exiting." % domainname)

    # Get machine account, sid, rid
    try:
        machineacct = s3db.getsampwnam("%s$" % netbiosname)
    except passdb.error:
        machinerid = None
        machinesid = None
    else:
        machinesid, machinerid = machineacct.user_sid.split()

    # Export account policy
    logger.info("Exporting account policy")
    policy = s3db.get_account_policy()

    # Export groups from old passdb backend
    logger.info("Exporting groups")
    grouplist = s3db.enum_group_mapping()
    groupmembers = {}
    for group in grouplist:
        sid, rid = group.sid.split()
        if sid == domainsid:
            if rid >= next_rid:
                next_rid = rid + 1

        # Get members for each group/alias
        if group.sid_name_use == lsa.SID_NAME_ALIAS:
            try:
                members = s3db.enum_aliasmem(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e)
                continue
        elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
            try:
                members = s3db.enum_group_members(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e)
                continue
        elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
            (group_dom_sid, rid) = group.sid.split()
            if group_dom_sid != security.dom_sid(security.SID_BUILTIN):
                logger.warn(
                    "Ignoring 'well known' group '%s' (should already be in AD, and have no members)", group.nt_name
                )
                continue
            # A number of buggy databases mix up well known groups and aliases.
            try:
                members = s3db.enum_aliasmem(group.sid)
                groupmembers[str(group.sid)] = members
            except passdb.error as e:
                logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e)
                continue
        else:
            logger.warn("Ignoring group '%s' %s with sid_name_use=%d", group.nt_name, group.sid, group.sid_name_use)
            continue

    # Export users from old passdb backend
    logger.info("Exporting users")
    userlist = s3db.search_users(0)
    userdata = {}
    uids = {}
    admin_user = None
    for entry in userlist:
        if machinerid and machinerid == entry["rid"]:
            continue
        username = entry["account_name"]
        if entry["rid"] < 1000:
            logger.info("  Skipping wellknown rid=%d (for username=%s)", entry["rid"], username)
            continue
        if entry["rid"] >= next_rid:
            next_rid = entry["rid"] + 1

        user = s3db.getsampwnam(username)
        acct_type = user.acct_ctrl & (samr.ACB_NORMAL | samr.ACB_WSTRUST | samr.ACB_SVRTRUST | samr.ACB_DOMTRUST)
        if acct_type == samr.ACB_SVRTRUST:
            logger.warn(
                "  Demoting BDC account trust for %s, this DC must be elevated to an AD DC using 'samba-tool domain dcpromo'"
                % username[:-1]
            )
            user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_SVRTRUST) | samr.ACB_WSTRUST

        elif acct_type == samr.ACB_DOMTRUST:
            logger.warn(
                "  Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust"
                % username[:-1]
            )
            continue

        elif acct_type == (samr.ACB_WSTRUST) and username[-1] != "$":
            logger.warn(
                "  Skipping account %s that has ACB_WSTRUST (W) set but does not end in $.  This account can not have worked, and is probably left over from a misconfiguration."
                % username
            )
            continue

        elif acct_type == (samr.ACB_NORMAL | samr.ACB_WSTRUST) and username[-1] == "$":
            logger.warn(
                "  Fixing account %s which had both ACB_NORMAL (U) and ACB_WSTRUST (W) set.  Account will be marked as ACB_WSTRUST (W), i.e. as a domain member"
                % username
            )
            user.acct_ctrl = user.acct_ctrl & ~samr.ACB_NORMAL

        elif acct_type == (samr.ACB_NORMAL | samr.ACB_SVRTRUST) and username[-1] == "$":
            logger.warn(
                "  Fixing account %s which had both ACB_NORMAL (U) and ACB_SVRTRUST (S) set.  Account will be marked as ACB_WSTRUST (S), i.e. as a domain member"
                % username
            )
            user.acct_ctrl = user.acct_ctrl & ~samr.ACB_NORMAL

        elif acct_type == 0 and username[-1] != "$":
            user.acct_ctrl = user.acct_ctrl | samr.ACB_NORMAL

        elif acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST:
            pass

        else:
            raise ProvisioningError(
                """Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of
ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X).

Please fix this account before attempting to upgrade again
"""
                % (username, user.acct_ctrl, samr.ACB_NORMAL, samr.ACB_WSTRUST, samr.ACB_SVRTRUST, samr.ACB_DOMTRUST)
            )

        userdata[username] = user
        try:
            uids[username] = s3db.sid_to_id(user.user_sid)[0]
        except passdb.error:
            try:
                uids[username] = pwd.getpwnam(username).pw_uid
            except KeyError:
                pass

        if not admin_user and username.lower() == "root":
            admin_user = username
        if username.lower() == "administrator":
            admin_user = username

        try:
            group_memberships = s3db.enum_group_memberships(user)
            for group in group_memberships:
                if str(group) in groupmembers:
                    if user.user_sid not in groupmembers[str(group)]:
                        groupmembers[str(group)].append(user.user_sid)
                else:
                    groupmembers[str(group)] = [user.user_sid]
        except passdb.error as e:
            logger.warn("Ignoring group memberships of '%s' %s: %s", username, user.user_sid, e)

    logger.info("Next rid = %d", next_rid)

    # Check for same username/groupname
    group_names = set([g.nt_name for g in grouplist])
    user_names = set([u["account_name"] for u in userlist])
    common_names = group_names.intersection(user_names)
    if common_names:
        logger.error("Following names are both user names and group names:")
        for name in common_names:
            logger.error("   %s" % name)
        raise ProvisioningError("Please remove common user/group names before upgrade.")

    # Check for same user sid/group sid
    group_sids = set([str(g.sid) for g in grouplist])
    if len(grouplist) != len(group_sids):
        raise ProvisioningError("Please remove duplicate group sid entries before upgrade.")
    user_sids = set(["%s-%u" % (domainsid, u["rid"]) for u in userlist])
    if len(userlist) != len(user_sids):
        raise ProvisioningError("Please remove duplicate user sid entries before upgrade.")
    common_sids = group_sids.intersection(user_sids)
    if common_sids:
        logger.error("Following sids are both user and group sids:")
        for sid in common_sids:
            logger.error("   %s" % str(sid))
        raise ProvisioningError("Please remove duplicate sid entries before upgrade.")

    # Get posix attributes from ldap or the os
    homes = {}
    shells = {}
    pgids = {}
    if ldap:
        creds = Credentials()
        creds.guess(samba3.lp)
        creds.set_bind_dn(ldapuser)
        creds.set_password(ldappass)
        urls = samba3.lp.get("passdb backend").split(":", 1)[1].strip('"')
        for url in urls.split():
            try:
                ldb_object = Ldb(url, credentials=creds)
            except ldb.LdbError as e:
                raise ProvisioningError("Could not open ldb connection to %s, the error message is: %s" % (url, e))
            else:
                break
    logger.info("Exporting posix attributes")
    userlist = s3db.search_users(0)
    for entry in userlist:
        username = entry["account_name"]
        if username in uids.keys():
            try:
                if ldap:
                    homes[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "homeDirectory"
                    )
                else:
                    homes[username] = pwd.getpwnam(username).pw_dir
            except KeyError:
                pass
            except IndexError:
                pass

            try:
                if ldap:
                    shells[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "loginShell"
                    )
                else:
                    shells[username] = pwd.getpwnam(username).pw_shell
            except KeyError:
                pass
            except IndexError:
                pass

            try:
                if ldap:
                    pgids[username] = get_posix_attr_from_ldap_backend(
                        logger, ldb_object, base_dn, username, "gidNumber"
                    )
                else:
                    pgids[username] = pwd.getpwnam(username).pw_gid
            except KeyError:
                pass
            except IndexError:
                pass

    logger.info("Reading WINS database")
    samba3_winsdb = None
    try:
        samba3_winsdb = samba3.get_wins_db()
    except IOError as e:
        logger.warn("Cannot open wins database, Ignoring: %s", str(e))

    if not (serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC"):
        dns_backend = "NONE"

    # If we found an admin user, set a fake pw that we will override.
    # This avoids us printing out an admin password that we won't actually
    # set.
    if admin_user:
        adminpass = generate_random_password(12, 32)
    else:
        adminpass = None

    # Do full provision
    result = provision(
        logger,
        session_info,
        targetdir=targetdir,
        realm=realm,
        domain=domainname,
        domainsid=domainsid,
        next_rid=next_rid,
        dc_rid=machinerid,
        adminpass=adminpass,
        dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
        hostname=netbiosname.lower(),
        machinepass=machinepass,
        serverrole=serverrole,
        samdb_fill=FILL_FULL,
        useeadb=useeadb,
        dns_backend=dns_backend,
        use_rfc2307=True,
        use_ntvfs=use_ntvfs,
        skip_sysvolacl=True,
    )
    result.report_logger(logger)

    # Import WINS database
    logger.info("Importing WINS database")

    if samba3_winsdb:
        import_wins(Ldb(result.paths.winsdb), samba3_winsdb)

    # Set Account policy
    logger.info("Importing Account policy")
    import_sam_policy(result.samdb, policy, logger)

    # Migrate IDMAP database
    logger.info("Importing idmap database")
    import_idmap(result.idmap, samba3, logger)

    # Set the s3 context for samba4 configuration
    new_lp_ctx = s3param.get_context()
    new_lp_ctx.load(result.lp.configfile)
    new_lp_ctx.set("private dir", result.lp.get("private dir"))
    new_lp_ctx.set("state directory", result.lp.get("state directory"))
    new_lp_ctx.set("lock directory", result.lp.get("lock directory"))

    # Connect to samba4 backend
    s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))

    # Start a new transaction (should speed this up a little, due to index churn)
    result.samdb.transaction_start()

    logger.info("Adding groups")
    try:
        # Export groups to samba4 backend
        logger.info("Importing groups")
        for g in grouplist:
            # Ignore uninitialized groups (gid = -1)
            if g.gid != -1:
                add_group_from_mapping_entry(result.samdb, g, logger)
                add_ad_posix_idmap_entry(result.samdb, g.sid, g.gid, "ID_TYPE_GID", logger)
                add_posix_attrs(
                    samdb=result.samdb,
                    sid=g.sid,
                    name=g.nt_name,
                    nisdomain=domainname.lower(),
                    xid_type="ID_TYPE_GID",
                    logger=logger,
                )

    except:
        # We need this, so that we do not give even more errors due to not cancelling the transaction
        result.samdb.transaction_cancel()
        raise

    logger.info("Committing 'add groups' transaction to disk")
    result.samdb.transaction_commit()

    logger.info("Adding users")
    # Start a new transaction (should speed this up a little, due to index churn)
    result.samdb.transaction_start()

    try:
        # Export users to samba4 backend
        logger.info("Importing users")
        for username in userdata:
            if username.lower() == "administrator":
                if userdata[username].user_sid != dom_sid(str(domainsid) + "-500"):
                    logger.error(
                        "User 'Administrator' in your existing directory has SID %s, expected it to be %s"
                        % (userdata[username].user_sid, dom_sid(str(domainsid) + "-500"))
                    )
                    raise ProvisioningError(
                        "User 'Administrator' in your existing directory does not have SID ending in -500"
                    )
            if username.lower() == "root":
                if userdata[username].user_sid == dom_sid(str(domainsid) + "-500"):
                    logger.warn("User root has been replaced by Administrator")
                else:
                    logger.warn(
                        "User root has been kept in the directory, it should be removed in favour of the Administrator user"
                    )

            s4_passdb.add_sam_account(userdata[username])
            if username in uids:
                add_ad_posix_idmap_entry(
                    result.samdb, userdata[username].user_sid, uids[username], "ID_TYPE_UID", logger
                )
                if (
                    (username in homes)
                    and (homes[username] is not None)
                    and (username in shells)
                    and (shells[username] is not None)
                    and (username in pgids)
                    and (pgids[username] is not None)
                ):
                    add_posix_attrs(
                        samdb=result.samdb,
                        sid=userdata[username].user_sid,
                        name=username,
                        nisdomain=domainname.lower(),
                        xid_type="ID_TYPE_UID",
                        home=homes[username],
                        shell=shells[username],
                        pgid=pgids[username],
                        logger=logger,
                    )

    except:
        # We need this, so that we do not give even more errors due to not cancelling the transaction
        result.samdb.transaction_cancel()
        raise

    logger.info("Committing 'add users' transaction to disk")
    result.samdb.transaction_commit()

    logger.info("Adding users to groups")
    # Start a new transaction (should speed this up a little, due to index churn)
    result.samdb.transaction_start()

    try:
        for g in grouplist:
            if str(g.sid) in groupmembers:
                add_users_to_group(result.samdb, g, groupmembers[str(g.sid)], logger)

    except:
        # We need this, so that we do not give even more errors due to not cancelling the transaction
        result.samdb.transaction_cancel()
        raise

    logger.info("Committing 'add users to groups' transaction to disk")
    result.samdb.transaction_commit()

    # Set password for administrator
    if admin_user:
        logger.info("Setting password for administrator")
        admin_userdata = s4_passdb.getsampwnam("administrator")
        admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
        if userdata[admin_user].lanman_passwd:
            admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
        admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time
        if userdata[admin_user].pw_history:
            admin_userdata.pw_history = userdata[admin_user].pw_history
        s4_passdb.update_sam_account(admin_userdata)
        logger.info("Administrator password has been set to password of user '%s'", admin_user)

    if result.server_role == "active directory domain controller":
        setsysvolacl(
            result.samdb,
            result.paths.netlogon,
            result.paths.sysvol,
            result.paths.root_uid,
            result.paths.root_gid,
            security.dom_sid(result.domainsid),
            result.names.dnsdomain,
            result.names.domaindn,
            result.lp,
            use_ntvfs,
        )