Exemple #1
0
def newprovision(names, creds, session, smbconf, provdir, logger):
    """Create a new provision.

    This provision will be the reference for knowing what has changed in the
    since the latest upgrade in the current provision

    :param names: List of provision parameters
    :param creds: Credentials for the authentification
    :param session: Session object
    :param smbconf: Path to the smb.conf file
    :param provdir: Directory where the provision will be stored
    :param logger: A Logger
    """
    if os.path.isdir(provdir):
        shutil.rmtree(provdir)
    os.mkdir(provdir)
    logger.info("Provision stored in %s", provdir)
    dns_backend="BIND9_DLZ"
    provision(logger, session, creds, smbconf=smbconf,
            targetdir=provdir, samdb_fill=FILL_FULL, realm=names.realm,
            domain=names.domain, domainguid=names.domainguid,
            domainsid=str(names.domainsid), ntdsguid=names.ntdsguid,
            policyguid=names.policyid, policyguid_dc=names.policyid_dc,
            hostname=names.netbiosname.lower(), hostip=None, hostip6=None,
            invocationid=names.invocation, adminpass=names.adminpass,
            krbtgtpass=None, machinepass=None, dnspass=None, root=None,
            nobody=None, wheel=None, users=None,
            serverrole="domain controller", 
            backend_type=None, ldapadminpass=None, ol_mmr_urls=None,
            slapd_path=None, 
            dom_for_fun_level=names.domainlevel, dns_backend=dns_backend,
            useeadb=True)
def newprovision(names, creds, session, smbconf, provdir, logger):
    """Create a new provision.

    This provision will be the reference for knowing what has changed in the
    since the latest upgrade in the current provision

    :param names: List of provision parameters
    :param creds: Credentials for the authentification
    :param session: Session object
    :param smbconf: Path to the smb.conf file
    :param provdir: Directory where the provision will be stored
    :param logger: A Logger
    """
    if os.path.isdir(provdir):
        shutil.rmtree(provdir)
    os.mkdir(provdir)
    logger.info("Provision stored in %s", provdir)
    provision(logger, session, creds, smbconf=smbconf,
            targetdir=provdir, samdb_fill=FILL_FULL, realm=names.realm,
            domain=names.domain, domainguid=names.domainguid,
            domainsid=str(names.domainsid), ntdsguid=names.ntdsguid,
            policyguid=names.policyid, policyguid_dc=names.policyid_dc,
            hostname=names.netbiosname.lower(), hostip=None, hostip6=None,
            invocationid=names.invocation, adminpass=names.adminpass,
            krbtgtpass=None, machinepass=None, dnspass=None, root=None,
            nobody=None, wheel=None, users=None,
            serverrole="domain controller", ldap_backend_extra_port=None,
            backend_type=None, ldapadminpass=None, ol_mmr_urls=None,
            slapd_path=None, setup_ds_path=None, nosync=None,
            dom_for_fun_level=names.domainlevel,
            ldap_dryrun_mode=None, useeadb=True)
Exemple #3
0
    def join_provision(ctx):
        '''provision the local SAM'''

        print "Calling bare provision"

        logger = logging.getLogger("provision")
        logger.addHandler(logging.StreamHandler(sys.stdout))
        smbconf = ctx.lp.configfile

        presult = provision(logger,
                            system_session(),
                            None,
                            smbconf=smbconf,
                            targetdir=ctx.targetdir,
                            samdb_fill=FILL_DRS,
                            realm=ctx.realm,
                            rootdn=ctx.root_dn,
                            domaindn=ctx.base_dn,
                            schemadn=ctx.schema_dn,
                            configdn=ctx.config_dn,
                            serverdn=ctx.server_dn,
                            domain=ctx.domain_name,
                            hostname=ctx.myname,
                            domainsid=ctx.domsid,
                            machinepass=ctx.acct_pass,
                            serverrole="domain controller",
                            sitename=ctx.site,
                            lp=ctx.lp)
        print "Provision OK for domain DN %s" % presult.domaindn
        ctx.local_samdb = presult.samdb
        ctx.lp = presult.lp
        ctx.paths = presult.paths
Exemple #4
0
 def setUp(self):
     super(SamDBTestCase, self).setUp()
     self.session = system_session()
     logger = logging.getLogger("selftest")
     domain = "dsdb"
     realm = "dsdb.samba.example.com"
     host_name = "test"
     server_role = "active directory domain controller"
     self.result = provision(logger,
                             self.session, targetdir=self.tempdir,
                             realm=realm, domain=domain,
                             hostname=host_name,
                             use_ntvfs=True,
                             serverrole=server_role,
                             dns_backend="SAMBA_INTERNAL",
                             dom_for_fun_level=DS_DOMAIN_FUNCTION_2008_R2)
     self.samdb = self.result.samdb
     self.lp = self.result.lp
Exemple #5
0
 def setUp(self):
     super(SamDBTestCase, self).setUp()
     self.session = system_session()
     logger = logging.getLogger("selftest")
     domain = "dsdb"
     realm = "dsdb.samba.example.com"
     host_name = "test"
     server_role = "active directory domain controller"
     dns_backend = "SAMBA_INTERNAL"
     self.result = provision(logger,
                             self.session, targetdir=self.tempdir,
                             realm=realm, domain=domain,
                             hostname=host_name,
                             use_ntvfs=True,
                             serverrole=server_role,
                             dns_backend="SAMBA_INTERNAL",
                             dom_for_fun_level=DS_DOMAIN_FUNCTION_2008_R2)
     self.samdb = self.result.samdb
     self.lp = self.result.lp
Exemple #6
0
    def join_provision(ctx):
        '''provision the local SAM'''

        print "Calling bare provision"

        logger = logging.getLogger("provision")
        logger.addHandler(logging.StreamHandler(sys.stdout))
        smbconf = ctx.lp.configfile

        presult = provision(logger, system_session(), None,
                            smbconf=smbconf, targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
                            realm=ctx.realm, rootdn=ctx.root_dn, domaindn=ctx.base_dn,
                            schemadn=ctx.schema_dn,
                            configdn=ctx.config_dn,
                            serverdn=ctx.server_dn, domain=ctx.domain_name,
                            hostname=ctx.myname, domainsid=ctx.domsid,
                            machinepass=ctx.acct_pass, serverrole="domain controller",
                            sitename=ctx.site, lp=ctx.lp)
        print "Provision OK for domain DN %s" % presult.domaindn
        ctx.local_samdb = presult.samdb
        ctx.lp          = presult.lp
        ctx.paths       = presult.paths
Exemple #7
0
        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=str(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)
Exemple #8
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)
Exemple #9
0
def upgrade_provision(samba3, logger, credentials, session_info, smbconf,
                      targetdir):
    oldconf = samba3.get_conf()

    if oldconf.get("domain logons") == "True":
        serverrole = "domain controller"
    else:
        if oldconf.get("security") == "user":
            serverrole = "standalone"
        else:
            serverrole = "member server"

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

    secrets_db = samba3.get_secrets_db()

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

    if realm is None:
        if oldconf.get("domain logons") == "True":
            logger.warning(
                "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 (generally it's the upcased DNS domainname)."
            )
            return
        else:
            realm = domainname.upper()
            logger.warning(
                "No realm specified in smb.conf file, assuming '%s'", realm)

    domainguid = secrets_db.get_domain_guid(domainname)
    domainsid = secrets_db.get_sid(domainname)
    if domainsid is None:
        logger.warning("Can't find domain secrets for '%s'; using random SID",
                       domainname)

    if netbiosname is not None:
        machinepass = secrets_db.get_machine_password(netbiosname)
    else:
        machinepass = None

    result = provision(logger=logger,
                       session_info=session_info,
                       credentials=credentials,
                       targetdir=targetdir,
                       realm=realm,
                       domain=domainname,
                       domainguid=domainguid,
                       domainsid=domainsid,
                       hostname=netbiosname,
                       machinepass=machinepass,
                       serverrole=serverrole)

    import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())

    # FIXME: import_registry(registry.Registry(), samba3.get_registry())

    # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)

    groupdb = samba3.get_groupmapping_db()
    for sid in groupdb.groupsids():
        (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
        # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)

    # FIXME: Aliases

    passdb = samba3.get_sam_db()
    for name in passdb:
        user = passdb[name]
        #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)

    if hasattr(passdb, 'ldap_url'):
        logger.info("Enabling Samba3 LDAP mappings for SAM database")

        enable_samba3sam(result.samdb, passdb.ldap_url)
Exemple #10
0
	def SetupDomain(self):
	
		#dom_for_fun_level
		result = True
		if self.opts.function_level is None:
			self.opts.dom_for_fun_level = DS_DOMAIN_FUNCTION_2003 #default
		elif self.opts.function_level == "2000":
			self.opts.dom_for_fun_level = DS_DOMAIN_FUNCTION_2000
		elif self.opts.function_level == "2003":
			self.opts.dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
		elif self.opts.function_level == "2008":
			self.opts.dom_for_fun_level = DS_DOMAIN_FUNCTION_2008
		elif self.opts.function_level == "2008_R2":
			self.opts.dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
			
		if self.opts.blank:
			 self.opts.samdb_fill = FILL_NT4SYNC
		elif self.opts.partitions_only:
			 self.opts.samdb_fill = FILL_DRS

		self.opts.eadb = True
		if self.opts.use_xattrs == "yes":
			 self.opts.eadb = False
		elif self.opts.use_xattrs == "auto" and not self.opts.lp.get("posix:eadb"):
			 file = tempfile.NamedTemporaryFile()
			 try:
					samba.ntacls.setntacl(self.opts.lp, file.name,
						"O:S-1-5-32G:S-1-5-32", "S-1-5-32", "native")
					self.opts.eadb = False
			 except:
					logger.info("You are not root or your system do not support xattr, using tdb backend for attributes. "
							 "If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
			 file.close()
		#logger.addHandler(logging.StreamHandler(sys.stdout))
		#if opts.quiet:
		#	logger.setLevel(logging.WARNING)
		#else:
		#	logger.setLevel(logging.INFO)
		
		# buffer debug messages so they can be sent with error emails
		#memory_handler = logging.handlers.MemoryHandler(1024*10)
		#memory_handler.setLevel(logging.DEBUG)
		# attach handlers
		msg = "data: start provision\n\n"
		response.write(msg);
		self.logger = logging.getLogger(__name__)
		LogOutput = ListBufferingHandler(1000);
		self.logger.addHandler(LogOutput)
		self.logger.setLevel(logging.WARNING)
		self.logger.setLevel(logging.INFO)

		
		try:
			provision(self.logger
					,self.session
					,self.creds
					, smbconf=self.opts.smbconf 
		#			,targetdir=self.opts.targetdir
					,samdb_fill=self.opts.samdb_fill
					,realm=self.opts.realm
					,domain=self.opts.domain,
		#			domainguid=self.opts.domain_guid, domainsid=self.opts.domain_sid,
		#			hostname=self.opts.host_name,
		#			hostip=self.opts.host_ip, hostip6=self.opts.host_ip6,
		#			ntdsguid=self.opts.ntds_guid,
		#			invocationid=self.opts.invocationid, 
					adminpass=self.opts.adminpass,
		#			krbtgtpass=self.opts.krbtgtpass, machinepass=self.opts.machinepass,
		#			dns_backend=self.opts.dns_backend,
		#			dnspass=self.opts.dnspass, root=self.opts.root, nobody=self.opts.nobody,
		#			wheel=self.opts.wheel, users=self.opts.users,
					serverrole=self.opts.server_role, dom_for_fun_level=self.opts.dom_for_fun_level,
		#			ldap_backend_extra_port=self.opts.ldap_backend_extra_port,
		#			ldap_backend_forced_uri=self.opts.ldap_backend_forced_uri,
		#			backend_type=self.opts.ldap_backend_type,
		#			ldapadminpass=self.opts.ldapadminpass, ol_mmr_urls=self.opts.ol_mmr_urls,
		#			slapd_path=self.opts.slapd_path, setup_ds_path=self.opts.setup_ds_path,
		#			nosync=self.opts.ldap_backend_nosync, ldap_dryrun_mode=self.opts.ldap_dryrun_mode,
					useeadb=self.opts.eadb, 
		#			next_rid=self.opts.next_rid,
					lp=self.opts.lp)
		#except ProvisioningError, e:
		except Exception,e:
			if(len(e.args)>1):
				self.SetError(e.args[1],e.args[0])
				self.logger.warning('Error: %s :%s',e.args[1], e.args[0])
			else:
				self.logger.warning('Error: %s :%s',e.args, 0)
				self.SetError(e.args,0)
			result = False;
			import traceback
			TraceBackLines = traceback.format_exc().splitlines()
			for t in TraceBackLines:
					self.logger.warning('Error: %s',t)
Exemple #11
0
def upgrade_from_samba3(samba3, logger, targetdir, session_info=None):
    """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
    """

    if samba3.lp.get("domain logons"):
        serverrole = "domain controller"
    else:
        if samba3.lp.get("security") == "user":
            serverrole = "standalone"
        else:
            serverrole = "member server"

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

    # secrets db
    secrets_db = samba3.get_secrets_db()

    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 == "domain controller":
            logger.warning("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 (generally it's the upcased DNS domainname).")
            return
        else:
            realm = domainname.upper()
            logger.warning("No realm specified in smb.conf file, assuming '%s'",
                    realm)

    # Find machine account and password
    machinepass = None
    machinerid = None
    machinesid = None
    next_rid = 1000

    try:
        machinepass = secrets_db.get_machine_password(netbiosname)
    except:
        pass

    # 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)
        machinesid, machinerid = machineacct.user_sid.split()
    except:
        pass

    # 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:
            members = s3db.enum_aliasmem(group.sid)
        elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
            try:
                members = s3db.enum_group_members(group.sid)
            except:
                continue
        elif group.sid_name_use == lsa.SID_NAME_WKN_GRP:
            logger.warn("Ignoring 'well known' group '%s' (should already be in AD, and have no members)",
                        group.nt_name, group.sid_name_use)
            continue
        else:
            logger.warn("Ignoring group '%s' with sid_name_use=%d",
                        group.nt_name, group.sid_name_use)
            continue
        groupmembers[group.nt_name] = members


    # 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
        
        userdata[username] = s3db.getsampwnam(username)
        try:
            uids[username] = s3db.sid_to_id(userdata[username].user_sid)[0]
        except:
            try:
                uids[username] = pwd.getpwnam(username).pw_uid
            except:
                pass

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

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

    # Do full provision
    result = provision(logger, session_info, None,
                       targetdir=targetdir, realm=realm, domain=domainname,
                       domainsid=str(domainsid), next_rid=next_rid,
                       dc_rid=machinerid,
                       hostname=netbiosname, machinepass=machinepass,
                       serverrole=serverrole, samdb_fill=FILL_FULL)

    # Import WINS database
    logger.info("Importing WINS database")
    import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())

    # 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"))

    # Export groups to samba4 backend
    logger.info("Importing groups")
    for g in grouplist:
        # Ignore uninitialized groups (gid = -1)
        if g.gid != 0xffffffff:
            add_idmap_entry(result.idmap, g.sid, g.gid, "GID", logger)
            add_group_from_mapping_entry(result.samdb, g, logger)

    # Export users to samba4 backend
    logger.info("Importing users")
    for username in userdata:
        if username.lower() == 'administrator' or username.lower() == 'root':
            continue
        s4_passdb.add_sam_account(userdata[username])
        if username in uids:
            add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "UID", logger)

    logger.info("Adding users to groups")
    for g in grouplist:
        if g.nt_name in groupmembers:
            add_users_to_group(result.samdb, g, groupmembers[g.nt_name], logger)

    # 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)
Exemple #12
0
    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.")

    if serverrole == "domain controller":
        dns_backend = "BIND9_FLATFILE"
    else:
        dns_backend = "NONE"

    # Do full provision
    result = provision(logger, session_info, None,
                       targetdir=targetdir, realm=realm, domain=domainname,
                       domainsid=str(domainsid), next_rid=next_rid,
                       dc_rid=machinerid,
                       dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
                       hostname=netbiosname, machinepass=machinepass,
                       serverrole=serverrole, samdb_fill=FILL_FULL,
                       useeadb=useeadb, dns_backend=dns_backend)

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

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

    if samba3_winsdb:
        import_wins(Ldb(result.paths.winsdb), samba3_winsdb)
Exemple #13
0
            "Please remove duplicate sid entries before upgrade.")

    if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC":
        dns_backend = "BIND9_DLZ"
    else:
        dns_backend = "NONE"

    # Do full provision
    result = provision(logger,
                       session_info,
                       None,
                       targetdir=targetdir,
                       realm=realm,
                       domain=domainname,
                       domainsid=str(domainsid),
                       next_rid=next_rid,
                       dc_rid=machinerid,
                       dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003,
                       hostname=netbiosname,
                       machinepass=machinepass,
                       serverrole=serverrole,
                       samdb_fill=FILL_FULL,
                       useeadb=useeadb,
                       dns_backend=dns_backend)

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

    samba3_winsdb = None
    try:
        samba3_winsdb = samba3.get_wins_db()
    except IOError, e:
Exemple #14
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,
        )
Exemple #15
0
    if admin_user:
        adminpass = generate_random_password(12, 32)
    else:
        adminpass = None

    # Do full provision
    result = provision(logger,
                       session_info,
                       None,
                       targetdir=targetdir,
                       realm=realm,
                       domain=domainname,
                       domainsid=str(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:
Exemple #16
0
def upgrade_provision(samba3, logger, credentials, session_info,
                      smbconf, targetdir):
    oldconf = samba3.get_conf()

    if oldconf.get("domain logons") == "True":
        serverrole = "domain controller"
    else:
        if oldconf.get("security") == "user":
            serverrole = "standalone"
        else:
            serverrole = "member server"

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

    secrets_db = samba3.get_secrets_db()

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

    if realm is None:
        if oldconf.get("domain logons") == "True":
            logger.warning("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 (generally it's the upcased DNS domainname).")
            return
        else:
            realm = domainname.upper()
            logger.warning("No realm specified in smb.conf file, assuming '%s'",
                    realm)

    domainguid = secrets_db.get_domain_guid(domainname)
    domainsid = secrets_db.get_sid(domainname)
    if domainsid is None:
        logger.warning("Can't find domain secrets for '%s'; using random SID",
            domainname)

    if netbiosname is not None:
        machinepass = secrets_db.get_machine_password(netbiosname)
    else:
        machinepass = None

    result = provision(logger=logger,
                       session_info=session_info, credentials=credentials,
                       targetdir=targetdir, realm=realm, domain=domainname,
                       domainguid=domainguid, domainsid=domainsid,
                       hostname=netbiosname, machinepass=machinepass,
                       serverrole=serverrole)

    import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())

    # FIXME: import_registry(registry.Registry(), samba3.get_registry())

    # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)

    groupdb = samba3.get_groupmapping_db()
    for sid in groupdb.groupsids():
        (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
        # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)

    # FIXME: Aliases

    passdb = samba3.get_sam_db()
    for name in passdb:
        user = passdb[name]
        #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)

    if hasattr(passdb, 'ldap_url'):
        logger.info("Enabling Samba3 LDAP mappings for SAM database")

        enable_samba3sam(result.samdb, passdb.ldap_url)
Exemple #17
0
def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, useeadb=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
    """

    if samba3.lp.get("domain logons"):
        serverrole = "domain controller"
    else:
        if samba3.lp.get("security") == "user":
            serverrole = "standalone"
        else:
            serverrole = "member server"

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

    # secrets db
    secrets_db = samba3.get_secrets_db()

    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 == "domain controller":
            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
    machinepass = None
    machinerid = None
    machinesid = None
    next_rid = 1000

    try:
        machinepass = secrets_db.get_machine_password(netbiosname)
    except:
        pass

    # 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)
        machinesid, machinerid = machineacct.user_sid.split()
    except:
        pass

    # 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:
            members = s3db.enum_aliasmem(group.sid)
        elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
            try:
                members = s3db.enum_group_members(group.sid)
            except:
                continue
            groupmembers[group.nt_name] = members
        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.
            members = s3db.enum_aliasmem(group.sid)
        else:
            logger.warn("Ignoring group '%s' with sid_name_use=%d",
                        group.nt_name, 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_NORMAL or acct_type == samr.ACB_WSTRUST or acct_type == samr.ACB_SVRTRUST):
            pass
        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_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)
        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
"""
                                    % (user.acct_flags, username,
                                       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:
            try:
                uids[username] = pwd.getpwnam(username).pw_uid
            except:
                pass

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

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

    # Check for same username/groupname
    group_names = set(map(lambda g: g.nt_name, grouplist))
    user_names = set(map(lambda u: u['account_name'], 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(map(lambda g: str(g.sid), grouplist))
    user_sids = set(map(lambda u: "%s-%u" % (domainsid, u['rid']), userlist))
    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.")

    # Do full provision
    result = provision(logger, session_info, None,
                       targetdir=targetdir, realm=realm, domain=domainname,
                       domainsid=str(domainsid), next_rid=next_rid,
                       dc_rid=machinerid,
                       hostname=netbiosname, machinepass=machinepass,
                       serverrole=serverrole, samdb_fill=FILL_FULL,
                       useeadb=useeadb)

    # Import WINS database
    logger.info("Importing WINS database")
    import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())

    # 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"))

    # Export groups to samba4 backend
    logger.info("Importing groups")
    for g in grouplist:
        # Ignore uninitialized groups (gid = -1)
        if g.gid != 0xffffffff:
            add_idmap_entry(result.idmap, g.sid, g.gid, "GID", logger)
            add_group_from_mapping_entry(result.samdb, g, logger)

    # Export users to samba4 backend
    logger.info("Importing users")
    for username in userdata:
        if username.lower() == 'administrator' or username.lower() == 'root':
            continue
        s4_passdb.add_sam_account(userdata[username])
        if username in uids:
            add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "UID", logger)

    logger.info("Adding users to groups")
    for g in grouplist:
        if g.nt_name in groupmembers:
            add_users_to_group(result.samdb, g, groupmembers[g.nt_name], logger)

    # 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)