Example #1
0
def get_prim_dom(secrets_path, lp):
    secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp)
    return secrets_ldb.search(base="CN=Primary Domains",
                              attrs=['objectClass', 'samAccountName',
                                     'secret', 'msDS-KeyVersionNumber'],
                              scope=ldb.SCOPE_SUBTREE,
                              expression="(objectClass=kerberosSecret)")
Example #2
0
def get_ldbs(paths, creds, session, lp):
    """Return LDB object mapped on most important databases

    :param paths: An object holding the different importants paths for provision object
    :param creds: Credential used for openning LDB files
    :param session: Session to use for openning LDB files
    :param lp: A loadparam object
    :return: A ProvisionLDB object that contains LDB object for the different LDB files of the provision"""

    ldbs = ProvisionLDB()

    ldbs.sam = SamDB(paths.samdb,
                     session_info=session,
                     credentials=creds,
                     lp=lp,
                     options=["modules:samba_dsdb"],
                     flags=0)
    ldbs.secrets = Ldb(paths.secrets,
                       session_info=session,
                       credentials=creds,
                       lp=lp)
    ldbs.idmap = Ldb(paths.idmapdb,
                     session_info=session,
                     credentials=creds,
                     lp=lp)
    ldbs.privilege = Ldb(paths.privilege,
                         session_info=session,
                         credentials=creds,
                         lp=lp)
    #    ldbs.hkcr = Ldb(paths.hkcr, session_info=session, credentials=creds, lp=lp)
    #    ldbs.hkcu = Ldb(paths.hkcu, session_info=session, credentials=creds, lp=lp)
    #    ldbs.hku = Ldb(paths.hku, session_info=session, credentials=creds, lp=lp)
    #    ldbs.hklm = Ldb(paths.hklm, session_info=session, credentials=creds, lp=lp)

    return ldbs
Example #3
0
def accountcontrol(lp, creds, username=None, value=0):
    """enable/disable an OpenChange user account.

    :param lp: Loadparm context
    :param creds: Credentials context
    :param username: Name of user to disable
    :param value: the control value
    """

    names = guess_names_from_smbconf(lp, None, None)
    db = Ldb(url=get_ldb_url(lp, creds, names),
             session_info=system_session(),
             credentials=creds,
             lp=lp)
    user_dn = get_user_dn(db, names.domaindn, username)
    extended_user = """
dn: %s
changetype: modify
replace: msExchUserAccountControl
msExchUserAccountControl: %d
""" % (user_dn, value)
    db.modify_ldif(extended_user)
    if value == 2:
        print "[+] Account %s disabled" % username
    else:
        print "[+] Account %s enabled" % username
Example #4
0
def accountcontrol(lp, creds, username=None, value=0):
    """enable/disable an OpenChange user account.

    :param lp: Loadparm context
    :param creds: Credentials context
    :param username: Name of user to disable
    :param value: the control value
    """

    names = guess_names_from_smbconf(lp, None, None)

    db = Ldb(url=os.path.join(lp.get("private dir"), lp.samdb_url()), 
             session_info=system_session(), credentials=creds, lp=lp)

    user_dn = "CN=%s,CN=Users,%s" % (username, names.domaindn)
    extended_user = """
dn: %s
changetype: modify
replace: msExchUserAccountControl
msExchUserAccountControl: %d
""" % (user_dn, value)
    db.modify_ldif(extended_user)
    if value == 2:
        print "[+] Account %s disabled" % username
    else:
        print "[+] Account %s enabled" % username
Example #5
0
        class Target:
            """Simple helper class that contains data for a specific SAM 
            connection."""

            def __init__(self, basedn, dn, lp):
                self.db = Ldb(lp=lp, session_info=system_session())
                self.basedn = basedn
                self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
                self.substvars = {"BASEDN": self.basedn}
                self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold)
                self.url = "tdb://" + self.file
                self._dn = dn

            def dn(self, rdn):
                return self._dn(self.basedn, rdn)

            def connect(self):
                return self.db.connect(self.url)

            def setup_data(self, path):
                self.add_ldif(read_datafile(path))

            def subst(self, text):
                return substitute_var(text, self.substvars)

            def add_ldif(self, ldif):
                self.db.add_ldif(self.subst(ldif))

            def modify_ldif(self, ldif):
                self.db.modify_ldif(self.subst(ldif))
 def __init__(self, file, basedn, dn):
     self.file = os.path.join(tempdir, file)
     self.url = "tdb://" + self.file
     self.basedn = basedn
     self.substvars = {"BASEDN": self.basedn}
     self.db = Ldb(lp=cmdline_loadparm)
     self._dn = dn
Example #7
0
def autenticacion(creds, lp):
    """
    Cumple con la idea de inyección, así que debería ser testeable
    """
    try:
        ldap_conn = Ldb('ldap://localhost', lp=lp, credentials=creds)
        
        domain_dn = ldap_conn.get_default_basedn()
        search_filter='sAMAccountName={0}'.format(creds.get_username())
       
        # NOTA: No intentes usar searchone para este caso específico. Dn resulta ser una clase no iterable
        busqueda = ldap_conn.search(base=domain_dn, scope=SCOPE_SUBTREE, expression=search_filter, attrs=['dn', 'memberOf', 'displayName'])
        user_dn = busqueda[0].dn
       
        sesion = user_session(ldap_conn, lp_ctx=lp, dn=user_dn, session_info_flags=session_info_flags)

        # Este punto podría ser importante para la idea de login
        token = sesion.security_token

    except LdbError as e:
        log.warning("Error LDB: %s" % e)
        return False;
    except IndexError as e:
        log.warning("El usuario %s no existe" % creds.get_username())
        return False;
    except Exception as e:
        log.warning("Error no contemplado %s " % e)
        return False;
   
    return busqueda
        class Target:
            """Simple helper class that contains data for a specific SAM 
            connection."""
            def __init__(self, file, basedn, dn):
                self.file = os.path.join(tempdir, file)
                self.url = "tdb://" + self.file
                self.basedn = basedn
                self.substvars = {"BASEDN": self.basedn}
                self.db = Ldb(lp=cmdline_loadparm)
                self._dn = dn

            def dn(self, rdn):
                return self._dn(self.basedn, rdn)

            def connect(self):
                return self.db.connect(self.url)

            def setup_data(self, path):
                self.add_ldif(read_datafile(path))

            def subst(self, text):
                return substitute_var(text, self.substvars)

            def add_ldif(self, ldif):
                self.db.add_ldif(self.subst(ldif))

            def modify_ldif(self, ldif):
                self.db.modify_ldif(self.subst(ldif))
Example #9
0
        class Target:
            """Simple helper class that contains data for a specific SAM 
            connection."""
            def __init__(self, file, basedn, dn):
                self.file = os.path.join(tempdir, file)
                self.url = "tdb://" + self.file
                self.basedn = basedn
                self.substvars = {"BASEDN": self.basedn}
                self.db = Ldb(lp=cmdline_loadparm)
                self._dn = dn

            def dn(self, rdn):
                return self._dn(self.basedn, rdn)

            def connect(self):
                return self.db.connect(self.url)

            def setup_data(self, path):
                self.add_ldif(read_datafile(path))

            def subst(self, text):
                return substitute_var(text, self.substvars)

            def add_ldif(self, ldif):
                self.db.add_ldif(self.subst(ldif))

            def modify_ldif(self, ldif):
                self.db.modify_ldif(self.subst(ldif))
Example #10
0
 def __init__(self,
              host,
              creds,
              lp,
              two=False,
              quiet=False,
              descriptor=False,
              sort_aces=False,
              verbose=False,
              view="section",
              base="",
              scope="SUB"):
     ldb_options = []
     samdb_url = host
     if not "://" in host:
         if os.path.isfile(host):
             samdb_url = "tdb://%s" % host
         else:
             samdb_url = "ldap://%s" % host
     # use 'paged_search' module when connecting remotely
     if samdb_url.lower().startswith("ldap://"):
         ldb_options = ["modules:paged_searches"]
     self.ldb = Ldb(url=samdb_url,
                    credentials=creds,
                    lp=lp,
                    options=ldb_options)
     self.search_base = base
     self.search_scope = scope
     self.two_domains = two
     self.quiet = quiet
     self.descriptor = descriptor
     self.sort_aces = sort_aces
     self.view = view
     self.verbose = verbose
     self.host = host
     self.base_dn = str(self.ldb.get_default_basedn())
     self.root_dn = str(self.ldb.get_root_basedn())
     self.config_dn = str(self.ldb.get_config_basedn())
     self.schema_dn = str(self.ldb.get_schema_basedn())
     self.domain_netbios = self.find_netbios()
     self.server_names = self.find_servers()
     self.domain_name = re.sub("[Dd][Cc]=", "",
                               self.base_dn).replace(",", ".")
     self.domain_sid = self.find_domain_sid()
     self.get_guid_map()
     self.get_sid_map()
     #
     # Log some domain controller specific place-holers that are being used
     # when compare content of two DCs. Uncomment for DEBUG purposes.
     if self.two_domains and not self.quiet:
         self.outf.write("\n* Place-holders for %s:\n" % self.host)
         self.outf.write(4 * " " +
                         "${DOMAIN_DN}      => %s\n" % self.base_dn)
         self.outf.write(4 * " " +
                         "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios)
         self.outf.write(4 * " " +
                         "${SERVER_NAME}     => %s\n" % self.server_names)
         self.outf.write(4 * " " +
                         "${DOMAIN_NAME}    => %s\n" % self.domain_name)
Example #11
0
 def setUp(self):
     super(MapTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
Example #12
0
    def init(self):
        # Check to see that this 'existing' LDAP backend in fact exists
        ldapi_db = Ldb(self.ldapi_uri)
        ldapi_db.search(base="", scope=SCOPE_BASE,
            expression="(objectClass=OpenLDAProotDSE)")

        # For now, assume existing backends at least emulate OpenLDAP
        self.ldap_backend_type = "openldap"
Example #13
0
    def init(self):
        # Check to see that this 'existing' LDAP backend in fact exists
        ldapi_db = Ldb(self.ldapi_uri)
        ldapi_db.search(base="", scope=SCOPE_BASE,
            expression="(objectClass=OpenLDAProotDSE)")

        # For now, assume existing backends at least emulate OpenLDAP
        self.ldap_backend_type = "openldap"
Example #14
0
 def __init__(self, basedn, dn):
     self.db = Ldb(lp=cmdline_loadparm)
     self.basedn = basedn
     self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
     self.substvars = {"BASEDN": self.basedn}
     self.file = os.path.join(tempdir,
                              "%s.ldb" % self.basedn_casefold)
     self.url = "tdb://" + self.file
     self._dn = dn
Example #15
0
 def __init__(self, basedn, dn, lp):
     self.db = Ldb(lp=lp, session_info=system_session())
     self.db.set_opaque("skip_allocate_sids", "true")
     self.basedn = basedn
     self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
     self.substvars = {"BASEDN": self.basedn}
     self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold)
     self.url = "tdb://" + self.file
     self._dn = dn
Example #16
0
    def init(self):
        from samba.provision import ProvisioningError
        # we will shortly start slapd with ldapi for final provisioning. first
        # check with ldapsearch -> rootDSE via self.ldap_uri if another
        # instance of slapd is already running
        try:
            ldapi_db = Ldb(self.ldap_uri)
            ldapi_db.search(base="", scope=SCOPE_BASE,
                            expression="(objectClass=OpenLDAProotDSE)")
            try:
                f = open(self.slapd_pid, "r")
            except IOError as err:
                if err != errno.ENOENT:
                    raise
            else:
                try:
                    p = f.read()
                finally:
                    f.close()
                self.logger.info("Check for slapd process with PID: %s and terminate it manually." % p)
            raise SlapdAlreadyRunning(self.ldap_uri)
        except LdbError:
            # XXX: We should never be catching all Ldb errors
            pass

        # Try to print helpful messages when the user has not specified the
        # path to slapd
        if self.slapd_path is None:
            raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
        if not os.path.exists(self.slapd_path):
            self.logger.warning("Path (%s) to slapd does not exist!",
                                self.slapd_path)

        if not os.path.isdir(self.ldapdir):
            os.makedirs(self.ldapdir, 0o700)

        # Put the LDIF of the schema into a database so we can search on
        # it to generate schema-dependent configurations in Fedora DS and
        # OpenLDAP
        schemadb_path = os.path.join(self.ldapdir, "schema-tmp.ldb")
        try:
            os.unlink(schemadb_path)
        except OSError:
            pass

        self.schema.write_to_tmp_ldb(schemadb_path)

        self.credentials = Credentials()
        self.credentials.guess(self.lp)
        # Kerberos to an ldapi:// backend makes no sense (we also force EXTERNAL)
        self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
        self.credentials.set_username("samba-admin")
        self.credentials.set_password(self.ldapadminpass)
        self.credentials.set_forced_sasl_mech("EXTERNAL")

        self.provision()
Example #17
0
    def init(self):
        from samba.provision import ProvisioningError
        # we will shortly start slapd with ldapi for final provisioning. first
        # check with ldapsearch -> rootDSE via self.ldap_uri if another
        # instance of slapd is already running
        try:
            ldapi_db = Ldb(self.ldap_uri)
            ldapi_db.search(base="", scope=SCOPE_BASE,
                expression="(objectClass=OpenLDAProotDSE)")
            try:
                f = open(self.slapd_pid, "r")
            except IOError as err:
                if err != errno.ENOENT:
                    raise
            else:
                try:
                    p = f.read()
                finally:
                    f.close()
                self.logger.info("Check for slapd process with PID: %s and terminate it manually." % p)
            raise SlapdAlreadyRunning(self.ldap_uri)
        except LdbError:
            # XXX: We should never be catching all Ldb errors
            pass

        # Try to print helpful messages when the user has not specified the
        # path to slapd
        if self.slapd_path is None:
            raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
        if not os.path.exists(self.slapd_path):
            self.logger.warning("Path (%s) to slapd does not exist!",
                self.slapd_path)

        if not os.path.isdir(self.ldapdir):
            os.makedirs(self.ldapdir, 0o700)

        # Put the LDIF of the schema into a database so we can search on
        # it to generate schema-dependent configurations in Fedora DS and
        # OpenLDAP
        schemadb_path = os.path.join(self.ldapdir, "schema-tmp.ldb")
        try:
            os.unlink(schemadb_path)
        except OSError:
            pass

        self.schema.write_to_tmp_ldb(schemadb_path)

        self.credentials = Credentials()
        self.credentials.guess(self.lp)
        # Kerberos to an ldapi:// backend makes no sense (we also force EXTERNAL)
        self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
        self.credentials.set_username("samba-admin")
        self.credentials.set_password(self.ldapadminpass)
        self.credentials.set_forced_sasl_mech("EXTERNAL")

        self.provision()
Example #18
0
 def setUp(self):
     super(MapTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
     ldb.set_opaque("skip_allocate_sids", "true")
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
     self.ldb.set_opaque("skip_allocate_sids", "true")
Example #19
0
    def __init__(self,
                 setup_path,
                 domain_sid,
                 schemadn=None,
                 serverdn=None,
                 files=None,
                 prefixmap=None):
        """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
        
        :param samdb: Load a schema into a SamDB.
        :param setup_path: Setup path function.
        :param schemadn: DN of the schema
        :param serverdn: DN of the server
        
        Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
        """

        self.schemadn = schemadn
        self.ldb = Ldb()
        self.schema_data = read_ms_schema(
            setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
            setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))

        if files is not None:
            for file in files:
                self.schema_data += open(file, 'r').read()

        self.schema_data = substitute_var(self.schema_data,
                                          {"SCHEMADN": schemadn})
        check_all_substituted(self.schema_data)

        self.schema_dn_modify = read_and_sub_file(
            setup_path("provision_schema_basedn_modify.ldif"), {
                "SCHEMADN": schemadn,
                "SERVERDN": serverdn,
            })

        descr = b64encode(get_schema_descriptor(domain_sid))
        self.schema_dn_add = read_and_sub_file(
            setup_path("provision_schema_basedn.ldif"), {
                "SCHEMADN": schemadn,
                "DESCRIPTOR": descr
            })

        self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()

        if prefixmap is not None:
            for map in prefixmap:
                self.prefixmap_data += "%s\n" % map

        self.prefixmap_data = b64encode(self.prefixmap_data)

        # We don't actually add this ldif, just parse it
        prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % self.prefixmap_data
        self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
Example #20
0
def newuser(names, lp, creds, username=None):
    """extend user record with OpenChange settings.

    :param lp: Loadparm context
    :param creds: Credentials context
    :param names: provision names object.
    :param username: Name of user to extend
    """

    db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(),
             credentials=creds, lp=lp)
    user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username)
    if user_dn:
        extended_user = """
dn: %(user_dn)s
changetype: modify
add: mailNickName
mailNickname: %(username)s
add: homeMDB
homeMDB: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s
add: homeMTA
homeMTA: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=%(firstou)s,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s
add: legacyExchangeDN
legacyExchangeDN: /o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s
add: proxyAddresses
proxyAddresses: =EX:/o=%(firstorg)s/ou=%(firstou)s/cn=Recipients/cn=%(username)s
proxyAddresses: smtp:postmaster@%(dnsdomain)s
proxyAddresses: X400:c=US;a= ;p=First Organizati;o=Exchange;s=%(username)s
proxyAddresses: SMTP:%(username)s@%(dnsdomain)s
replace: msExchUserAccountControl
msExchUserAccountControl: 0
"""
        ldif_value = extended_user % {"user_dn": user_dn,
                                      "username": username,
                                      "netbiosname": names.netbiosname,
                                      "firstorg": names.firstorg,
                                      "firstou": names.firstou,
                                      "domaindn": names.domaindn,
                                      "dnsdomain": names.dnsdomain}
        db.modify_ldif(ldif_value)

        res = db.search(base=user_dn, scope=SCOPE_BASE, attrs=["*"])
        if len(res) == 1:
            record = res[0]
        else:
            raise Exception("this should never happen as we just modified the record...")
        record_keys = map(lambda x: x.lower(), record.keys())

        if "displayname" not in record_keys:
            extended_user = "******" % (user_dn, username)
            db.modify_ldif(extended_user)

        if "mail" not in record_keys:
            extended_user = "******" % (user_dn, username, names.dnsdomain)
            db.modify_ldif(extended_user)

        print "[+] User %s extended and enabled" % username
    else:
        print "[!] User '%s' not found" % username
Example #21
0
    def init(self):
        #Check to see that this 'existing' LDAP backend in fact exists
        ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
        search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                            expression="(objectClass=OpenLDAProotDSE)")

        # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
        # This caused them to be set into the long-term database later in the script.
        self.secrets_credentials = self.credentials

        self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
Example #22
0
    def init(self):
        #Check to see that this 'existing' LDAP backend in fact exists
        ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
        search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                            expression="(objectClass=OpenLDAProotDSE)")

        # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
        # This caused them to be set into the long-term database later in the script.
        self.secrets_credentials = self.credentials

        self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
Example #23
0
    def start(self):
        from samba.provision import ProvisioningError
        self.slapd_command_escaped = "\'" + "\' \'".join(
            self.slapd_command) + "\'"
        ldap_backend_script = os.path.join(self.ldapdir,
                                           "ldap_backend_startup.sh")
        f = open(ldap_backend_script, 'w')
        try:
            f.write("#!/bin/sh\n" + self.slapd_command_escaped + " $@\n")
        finally:
            f.close()

        os.chmod(ldap_backend_script, 0o755)

        # Now start the slapd, so we can provision onto it.  We keep the
        # subprocess context around, to kill this off at the successful
        # end of the script
        self.slapd = subprocess.Popen(self.slapd_provision_command,
                                      close_fds=True,
                                      shell=False)

        count = 0
        while self.slapd.poll() is None:
            # Wait until the socket appears
            try:
                time.sleep(1)
                ldapi_db = Ldb(self.ldap_uri,
                               lp=self.lp,
                               credentials=self.credentials)
                ldapi_db.search(base="",
                                scope=SCOPE_BASE,
                                expression="(objectClass=OpenLDAProotDSE)")
                # If we have got here, then we must have a valid connection to
                # the LDAP server!
                return
            except LdbError:
                count = count + 1

                if count > 15:
                    self.logger.error(
                        "Could not connect to slapd started with: %s" % "\'" +
                        "\' \'".join(self.slapd_provision_command) + "\'")
                    raise ProvisioningError(
                        "slapd never accepted a connection within 15 seconds of starting"
                    )

        self.logger.error("Could not start slapd with: %s" % "\'" +
                          "\' \'".join(self.slapd_provision_command) + "\'")
        raise ProvisioningError(
            "slapd died before we could make a connection to it")
Example #24
0
    def run(self, secret, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        url = lp.get("secrets database")
        secretsdb = Ldb(url=url, session_info=system_session(),
            credentials=creds, lp=lp)
        
        result = secretsdb.search(attrs=["secret"], 
            expression="(&(objectclass=primaryDomain)(samaccountname=%s))" % secret)

        if len(result) != 1:
            raise CommandError("search returned %d records, expected 1" % len(result))

        self.outf.write("%s\n" % result[0]["secret"])
Example #25
0
    def start(self):
        from samba.provision import ProvisioningError
        self.slapd_command_escaped = "\'" + "\' \'".join(
            self.slapd_command) + "\'"
        ldap_backend_script = os.path.join(self.ldapdir,
                                           "ldap_backend_startup.sh")
        f = open(ldap_backend_script, 'w')
        try:
            f.write("#!/bin/sh\n" + self.slapd_command_escaped + " $@\n")
        finally:
            f.close()

        os.chmod(ldap_backend_script, 0o755)

        # Now start the slapd, so we can provision onto it.  We keep the
        # subprocess context around, to kill this off at the successful
        # end of the script
        self.slapd = subprocess.Popen(
            self.slapd_provision_command, close_fds=True, shell=False)

        count = 0
        while self.slapd.poll() is None:
            # Wait until the socket appears
            try:
                time.sleep(1)
                ldapi_db = Ldb(
                    self.ldap_uri, lp=self.lp, credentials=self.credentials)
                ldapi_db.search(
                    base="",
                    scope=SCOPE_BASE,
                    expression="(objectClass=OpenLDAProotDSE)")
                # If we have got here, then we must have a valid connection to
                # the LDAP server!
                return
            except LdbError:
                count = count + 1

                if count > 15:
                    self.logger.error(
                        "Could not connect to slapd started with: %s" % "\'" +
                        "\' \'".join(self.slapd_provision_command) + "\'")
                    raise ProvisioningError(
                        "slapd never accepted a connection within 15 seconds of starting"
                    )

        self.logger.error("Could not start slapd with: %s" % "\'" +
                          "\' \'".join(self.slapd_provision_command) + "\'")
        raise ProvisioningError(
            "slapd died before we could make a connection to it")
Example #26
0
def newuser(lp, creds, username=None):
    """extend user record with OpenChange settings.
    
    :param lp: Loadparm context
    :param creds: Credentials context
    :param username: Name of user to extend
    """

    names = guess_names_from_smbconf(lp, None, None)
    db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(), 
             credentials=creds, lp=lp)
    user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username)
    if user_dn:
        extended_user = """
dn: %(user_dn)s
changetype: modify
add: mailNickName
mailNickname: %(username)s
add: homeMDB
homeMDB: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s
add: homeMTA
homeMTA: CN=Mailbox Store (%(netbiosname)s),CN=First Storage Group,CN=InformationStore,CN=%(netbiosname)s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=%(firstorg)s,CN=Microsoft Exchange,CN=Services,CN=Configuration,%(domaindn)s
add: legacyExchangeDN
legacyExchangeDN: /o=%(firstorg)s/ou=First Administrative Group/cn=Recipients/cn=%(username)s
add: proxyAddresses
proxyAddresses: =EX:/o=%(firstorg)s/ou=First Administrative Group/cn=Recipients/cn=%(username)s
proxyAddresses: smtp:postmaster@%(dnsdomain)s
proxyAddresses: X400:c=US;a= ;p=First Organizati;o=Exchange;s=%(username)s
proxyAddresses: SMTP:%(username)s@%(dnsdomain)s
replace: msExchUserAccountControl
msExchUserAccountControl: 0
"""
        ldif_value = extended_user % {"user_dn": user_dn,
                                      "username": username,
                                      "netbiosname": names.netbiosname,
                                      "firstorg": names.firstorg,
                                      "domaindn": names.domaindn,
                                      "dnsdomain": names.dnsdomain}
        db.modify_ldif(ldif_value)

        res = db.search(base=user_dn, scope=SCOPE_BASE, attrs=["*"])
        if len(res) == 1:
            record = res[0]
        else:
            raise Exception, \
                "this should never happen as we just modified the record..."
        record_keys = map(lambda x: x.lower(), record.keys())

        if "displayname" not in record_keys:
            extended_user = "******" % (user_dn, username)
            db.modify_ldif(extended_user)

        if "mail" not in record_keys:
            extended_user = "******" % (user_dn, username, names.dnsdomain)
            db.modify_ldif(extended_user)

        print "[+] User %s extended and enabled" % username
    else:
        print "[!] User '%s' not found" % username
Example #27
0
 def __init__(self, file, basedn, dn):
     self.file = os.path.join(tempdir, file)
     self.url = "tdb://" + self.file
     self.basedn = basedn
     self.substvars = {"BASEDN": self.basedn}
     self.db = Ldb(lp=cmdline_loadparm)
     self._dn = dn
Example #28
0
    def join_finalise(ctx):
        '''finalise the join, mark us synchronised and setup secrets db'''

        print "Sending DsReplicateUpdateRefs for all the partitions"
        ctx.send_DsReplicaUpdateRefs(ctx.schema_dn)
        ctx.send_DsReplicaUpdateRefs(ctx.config_dn)
        ctx.send_DsReplicaUpdateRefs(ctx.base_dn)

        print "Setting isSynchronized and dsServiceName"
        m = ldb.Message()
        m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
        m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE,
                                                 "isSynchronized")
        m["dsServiceName"] = ldb.MessageElement(
            "<GUID=%s>" % str(ctx.ntds_guid), ldb.FLAG_MOD_REPLACE,
            "dsServiceName")
        ctx.local_samdb.modify(m)

        if ctx.subdomain:
            return

        secrets_ldb = Ldb(ctx.paths.secrets,
                          session_info=system_session(),
                          lp=ctx.lp)

        print "Setting up secrets database"
        secretsdb_self_join(secrets_ldb,
                            domain=ctx.domain_name,
                            realm=ctx.realm,
                            dnsdomain=ctx.dnsdomain,
                            netbiosname=ctx.myname,
                            domainsid=security.dom_sid(ctx.domsid),
                            machinepass=ctx.acct_pass,
                            secure_channel_type=ctx.secure_channel_type,
                            key_version_number=ctx.key_version_number)
Example #29
0
    def post_setup(self):
        ldapi_db = Ldb(self.ldap_uri, credentials=self.credentials)

        # configure in-directory access control on Fedora DS via the aci
        # attribute (over a direct ldapi:// socket)
        aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn

        m = ldb.Message()
        m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")

        for dnstring in (self.names.domaindn, self.names.configdn,
                         self.names.schemadn):
            m.dn = ldb.Dn(ldapi_db, dnstring)
            ldapi_db.modify(m)
        return LDAPBackendResult(self.credentials, self.slapd_command_escaped,
            self.ldapdir)
Example #30
0
    def join_provision_own_domain(ctx):
        """Provision the local SAM."""

        # we now operate exclusively on the local database, which
        # we need to reopen in order to get the newly created schema
        print("Reconnecting to local samdb")
        ctx.samdb = SamDB(url=ctx.local_samdb.url,
                          session_info=system_session(),
                          lp=ctx.local_samdb.lp,
                          global_schema=False)
        ctx.samdb.set_invocation_id(str(ctx.invocation_id))
        ctx.local_samdb = ctx.samdb

        print("Finding domain GUID from ncName")
        res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'],
                                     controls=["extended_dn:1:1"])
        domguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0]).get_extended_component('GUID')))
        print("Got domain GUID %s" % domguid)

        print("Calling own domain provision")

        logger = logging.getLogger("provision")
        logger.addHandler(logging.StreamHandler(sys.stdout))

        secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)

        presult = provision_fill(ctx.local_samdb, secrets_ldb,
                                 logger, ctx.names, ctx.paths, domainsid=security.dom_sid(ctx.domsid),
                                 domainguid=domguid,
                                 targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
                                 machinepass=ctx.acct_pass, serverrole="domain controller",
                                 lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
                                 dns_backend=ctx.dns_backend)
        print("Provision OK for domain %s" % ctx.names.dnsdomain)
Example #31
0
 def rsop(self, gpo):
     output = {}
     pol_file = 'MACHINE/Registry.pol'
     section = 'Software\Policies\Microsoft\Cryptography\AutoEnrollment'
     if gpo.file_sys_path:
         path = os.path.join(gpo.file_sys_path, pol_file)
         pol_conf = self.parse(path)
         if not pol_conf:
             return output
         for e in pol_conf.entries:
             if e.keyname == section and e.valuename == 'AEPolicy':
                 enroll = e.data & 0x1 == 1
                 if e.data == 0x8000 or not enroll:
                     continue
                 output['Auto Enrollment Policy'] = {}
                 url = 'ldap://%s' % get_dc_hostname(self.creds, self.lp)
                 ldb = Ldb(url=url, session_info=system_session(),
                           lp=self.lp, credentials=self.creds)
                 cas = fetch_certification_authorities(ldb)
                 for ca in cas:
                     policy = 'Auto Enrollment Policy'
                     cn = ca['cn'][0]
                     output[policy][cn] = {}
                     output[policy][cn]['CA Certificate'] = \
                         format_root_cert(ca['cACertificate'][0]).decode()
                     output[policy][cn]['Auto Enrollment Server'] = \
                         ca['dNSHostName'][0]
                     supported_templates = \
                         get_supported_templates(ca['dNSHostName'][0],
                                                 self.logger)
                     output[policy][cn]['Templates'] = \
                         [t.decode() for t in supported_templates]
     return output
Example #32
0
    def post_setup(self):
        ldapi_db = Ldb(self.ldap_uri, credentials=self.credentials)

        # configure in-directory access control on Fedora DS via the aci
        # attribute (over a direct ldapi:// socket)
        aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn

        m = ldb.Message()
        m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")

        for dnstring in (self.names.domaindn, self.names.configdn,
                         self.names.schemadn):
            m.dn = ldb.Dn(ldapi_db, dnstring)
            ldapi_db.modify(m)
        return LDAPBackendResult(self.credentials, self.slapd_command_escaped,
                                 self.ldapdir)
Example #33
0
    def process_group_policy(self, deleted_gpo_list, changed_gpo_list,
                             trust_dir=None, private_dir=None):
        if trust_dir is None:
            trust_dir = self.lp.cache_path('certs')
        if private_dir is None:
            private_dir = self.lp.private_path('certs')
        if not os.path.exists(trust_dir):
            os.mkdir(trust_dir, mode=0o755)
        if not os.path.exists(private_dir):
            os.mkdir(private_dir, mode=0o700)

        for guid, settings in deleted_gpo_list:
            self.gp_db.set_guid(guid)
            if str(self) in settings:
                for ca_cn_enc, data in settings[str(self)].items():
                    ca_cn = base64.b64decode(ca_cn_enc)
                    data = json.loads(data)
                    getcert = which('getcert')
                    if getcert is not None:
                        Popen([getcert, 'remove-ca', '-c', ca_cn]).wait()
                        for nickname in data['templates']:
                            Popen([getcert, 'stop-tracking',
                                   '-i', nickname]).wait()
                    for f in data['files']:
                        if os.path.exists(f):
                            os.unlink(f)
                    self.gp_db.delete(str(self), ca_cn_enc)
            self.gp_db.commit()

        for gpo in changed_gpo_list:
            if gpo.file_sys_path:
                section = 'Software\Policies\Microsoft\Cryptography\AutoEnrollment'
                self.gp_db.set_guid(gpo.name)
                pol_file = 'MACHINE/Registry.pol'
                path = os.path.join(gpo.file_sys_path, pol_file)
                pol_conf = self.parse(path)
                if not pol_conf:
                    continue
                for e in pol_conf.entries:
                    if e.keyname == section and e.valuename == 'AEPolicy':
                        # This policy applies as specified in [MS-CAESO] 4.4.5.1
                        if e.data == 0x8000:
                            continue # The policy is disabled
                        enroll = e.data & 0x1 == 1
                        manage = e.data & 0x2 == 1
                        retrive_pending = e.data & 0x4 == 1
                        if enroll:
                            url = 'ldap://%s' % get_dc_hostname(self.creds,
                                                                self.lp)
                            ldb = Ldb(url=url, session_info=system_session(),
                                      lp=self.lp, credentials=self.creds)
                            cas = fetch_certification_authorities(ldb)
                            for ca in cas:
                                data = cert_enroll(ca, trust_dir,
                                                   private_dir, self.logger)
                                self.gp_db.store(str(self),
                                     base64.b64encode(ca['cn'][0]).decode(),
                                     data)
                        self.gp_db.commit()
Example #34
0
 def __init__(self, basedn, dn):
     self.db = Ldb(lp=cmdline_loadparm, session_info=system_session())
     self.basedn = basedn
     self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
     self.substvars = {"BASEDN": self.basedn}
     self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold)
     self.url = "tdb://" + self.file
     self._dn = dn
Example #35
0
 def setUp(self):
     super(MapTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=cmdline_loadparm, session_info=system_session())
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm, session_info=system_session())
Example #36
0
    def run(self, secret, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        name = lp.get("secrets database")
        path = lp.get("private dir")
        url = os.path.join(path, name)
        if not os.path.exists(url):
            raise CommandError("secret database not found at %s " % url)
        secretsdb = Ldb(url=url, session_info=system_session(), credentials=creds, lp=lp)
        result = secretsdb.search(
            attrs=["secret"], expression="(&(objectclass=primaryDomain)(samaccountname=%s))" % ldb.binary_encode(secret)
        )

        if len(result) != 1:
            raise CommandError("search returned %d records, expected 1" % len(result))

        self.outf.write("%s\n" % result[0]["secret"])
Example #37
0
 def __init__(
     self,
     host,
     creds,
     lp,
     two=False,
     quiet=False,
     descriptor=False,
     sort_aces=False,
     verbose=False,
     view="section",
     base="",
     scope="SUB",
     outf=sys.stdout,
     errf=sys.stderr,
     skip_missing_dn=True,
 ):
     ldb_options = []
     samdb_url = host
     if not "://" in host:
         if os.path.isfile(host):
             samdb_url = "tdb://%s" % host
         else:
             samdb_url = "ldap://%s" % host
     # use 'paged_search' module when connecting remotely
     if samdb_url.lower().startswith("ldap://"):
         ldb_options = ["modules:paged_searches"]
     self.outf = outf
     self.errf = errf
     self.ldb = Ldb(url=samdb_url, credentials=creds, lp=lp, options=ldb_options)
     self.search_base = base
     self.search_scope = scope
     self.two_domains = two
     self.quiet = quiet
     self.descriptor = descriptor
     self.sort_aces = sort_aces
     self.view = view
     self.verbose = verbose
     self.host = host
     self.skip_missing_dn = skip_missing_dn
     self.base_dn = str(self.ldb.get_default_basedn())
     self.root_dn = str(self.ldb.get_root_basedn())
     self.config_dn = str(self.ldb.get_config_basedn())
     self.schema_dn = str(self.ldb.get_schema_basedn())
     self.domain_netbios = self.find_netbios()
     self.server_names = self.find_servers()
     self.domain_name = re.sub("[Dd][Cc]=", "", self.base_dn).replace(",", ".")
     self.domain_sid = self.find_domain_sid()
     self.get_sid_map()
     #
     # Log some domain controller specific place-holers that are being used
     # when compare content of two DCs. Uncomment for DEBUG purposes.
     if self.two_domains and not self.quiet:
         self.outf.write("\n* Place-holders for %s:\n" % self.host)
         self.outf.write(4 * " " + "${DOMAIN_DN}      => %s\n" % self.base_dn)
         self.outf.write(4 * " " + "${DOMAIN_NETBIOS} => %s\n" % self.domain_netbios)
         self.outf.write(4 * " " + "${SERVER_NAME}     => %s\n" % self.server_names)
         self.outf.write(4 * " " + "${DOMAIN_NAME}    => %s\n" % self.domain_name)
Example #38
0
        class Target:
            """Simple helper class that contains data for a specific SAM
            connection."""
            def __init__(self, basedn, dn, lp):
                self.db = Ldb(lp=lp, session_info=system_session())
                self.db.set_opaque("skip_allocate_sids", "true")
                self.basedn = basedn
                self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
                self.substvars = {"BASEDN": self.basedn}
                self.file = os.path.join(tempdir,
                                         "%s.ldb" % self.basedn_casefold)
                self.url = "tdb://" + self.file
                self._dn = dn

            def dn(self, rdn):
                return self._dn(self.basedn, rdn)

            def connect(self):
                return self.db.connect(self.url)

            def setup_data(self, path):
                self.add_ldif(read_datafile(path))

            def subst(self, text):
                return substitute_var(text, self.substvars)

            def add_ldif(self, ldif):
                self.db.add_ldif(self.subst(ldif))

            def modify_ldif(self, ldif):
                self.db.modify_ldif(self.subst(ldif))
Example #39
0
 def __init__(self, basedn, dn, lp):
     self.db = Ldb(lp=lp, session_info=system_session())
     self.db.set_opaque("skip_allocate_sids", "true");
     self.basedn = basedn
     self.basedn_casefold = ldb.Dn(self.db, basedn).get_casefold()
     self.substvars = {"BASEDN": self.basedn}
     self.file = os.path.join(tempdir, "%s.ldb" % self.basedn_casefold)
     self.url = "tdb://" + self.file
     self._dn = dn
Example #40
0
 def setUp(self):
     super(Samba3SamTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
     self.samba3.setup_data("samba3.ldif")
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
Example #41
0
 def setUp(self):
     super(MapTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
     self.templates.setup_data("provision_samba3sam_templates.ldif")
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
Example #42
0
 def setUp(self):
     super(MapTestCase, self).setUp()
     ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
     ldb.set_opaque("skip_allocate_sids", "true");
     ldif = read_datafile("provision_samba3sam.ldif")
     ldb.add_ldif(self.samba4.subst(ldif))
     self.setup_modules(ldb, self.samba3, self.samba4)
     del ldb
     self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
     self.ldb.set_opaque("skip_allocate_sids", "true");
Example #43
0
 def init(self):
     # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
     # if another instance of slapd is already running 
     try:
         ldapi_db = Ldb(self.ldapi_uri)
         search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                             expression="(objectClass=OpenLDAProotDSE)");
         try:
             f = open(self.paths.slapdpid, "r")
             p = f.read()
             f.close()
             self.message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
         except:
             pass
         
         raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
     
     except LdbError, e:
         pass
Example #44
0
class OpenchangeDBWithLDB(OpenchangeDB):
    def __init__(self, uri):
        self.ldb = Ldb(uri)

    def get_calendar_uri(self, usercn, email):
        base_dn = "CN=%s,%s" % (usercn, config["samba"]["oc_user_basedn"])
        ldb_filter = "(&(objectClass=systemfolder)(PidTagContainerClass=IPF.Appointment)(MAPIStoreURI=*))"
        res = self.ldb.search(base=base_dn, scope=ldb.SCOPE_SUBTREE,
                              expression=ldb_filter, attrs=["MAPIStoreURI"])
        return [str(res[x]["MAPIStoreURI"][0]) for x in xrange(len(res))]
Example #45
0
 def init(self):
     # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
     # if another instance of slapd is already running 
     try:
         ldapi_db = Ldb(self.ldapi_uri)
         search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                             expression="(objectClass=OpenLDAProotDSE)");
         try:
             f = open(self.paths.slapdpid, "r")
             p = f.read()
             f.close()
             self.message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
         except:
             pass
         
         raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
     
     except LdbError, e:
         pass
Example #46
0
    def start(self):
        self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
        open(self.paths.ldapdir + "/ldap_backend_startup.sh", 'w').write("#!/bin/sh\n" + self.slapd_command_escaped + "\n")

        # Now start the slapd, so we can provision onto it.  We keep the
        # subprocess context around, to kill this off at the successful
        # end of the script
        self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
    
        while self.slapd.poll() is None:
            # Wait until the socket appears
            try:
                ldapi_db = Ldb(self.ldapi_uri, lp=self.lp, credentials=self.credentials)
                search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                                    expression="(objectClass=OpenLDAProotDSE)")
                # If we have got here, then we must have a valid connection to the LDAP server!
                return
            except LdbError, e:
                time.sleep(1)
                pass
Example #47
0
    def start(self):
        self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
        open(self.paths.ldapdir + "/ldap_backend_startup.sh", 'w').write("#!/bin/sh\n" + self.slapd_command_escaped + "\n")

        # Now start the slapd, so we can provision onto it.  We keep the
        # subprocess context around, to kill this off at the successful
        # end of the script
        self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
    
        while self.slapd.poll() is None:
            # Wait until the socket appears
            try:
                ldapi_db = Ldb(self.ldapi_uri, lp=self.lp, credentials=self.credentials)
                search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
                                                    expression="(objectClass=OpenLDAProotDSE)")
                # If we have got here, then we must have a valid connection to the LDAP server!
                return
            except LdbError, e:
                time.sleep(1)
                pass
Example #48
0
 def run(self, acl, file, quiet=False,xattr_backend=None,eadb_file=None,
         credopts=None, sambaopts=None, versionopts=None):
     lp = sambaopts.get_loadparm()
     creds = credopts.get_credentials(lp)
     path = os.path.join(lp.get("private dir"), lp.get("secrets database") or "secrets.ldb")
     creds = credopts.get_credentials(lp)
     creds.set_kerberos_state(DONT_USE_KERBEROS)
     try:
         ldb = Ldb(path, session_info=system_session(), credentials=creds,
                   lp=lp)
     except Exception, e:
         raise CommandError("Unable to read domain SID from configuration files", e)
Example #49
0
class OpenchangeDBWithLDB(OpenchangeDB):
    def __init__(self, uri):
        self.ldb = Ldb(uri)

    def get_calendar_uri(self, usercn, email):
        base_dn = "CN=%s,%s" % (usercn, config["samba"]["oc_user_basedn"])
        ldb_filter = "(&(objectClass=systemfolder)(PidTagContainerClass=IPF.Appointment)(MAPIStoreURI=*))"
        res = self.ldb.search(base=base_dn,
                              scope=ldb.SCOPE_SUBTREE,
                              expression=ldb_filter,
                              attrs=["MAPIStoreURI"])
        return [str(res[x]["MAPIStoreURI"][0]) for x in xrange(len(res))]
Example #50
0
    def backup_smb_dbs(self, private_dir, samdb, lp, logger):
        # First, determine if DB backend is MDB.  Assume not unless there is a
        # 'backendStore' attribute on @PARTITION containing the text 'mdb'
        store_label = "backendStore"
        res = samdb.search(base="@PARTITION",
                           scope=ldb.SCOPE_BASE,
                           attrs=[store_label])
        mdb_backend = store_label in res[0] and res[0][store_label][0] == 'mdb'

        sam_ldb_path = os.path.join(private_dir, 'sam.ldb')
        copy_function = None
        if mdb_backend:
            logger.info('MDB backend detected.  Using mdb backup function.')
            copy_function = self.offline_mdb_copy
        else:
            logger.info('Starting transaction on ' + sam_ldb_path)
            copy_function = self.offline_tdb_copy
            sam_obj = Ldb(sam_ldb_path, lp=lp)
            sam_obj.transaction_start()

        logger.info('   backing up ' + sam_ldb_path)
        self.offline_tdb_copy(sam_ldb_path)
        sam_ldb_d = sam_ldb_path + '.d'
        for sam_file in os.listdir(sam_ldb_d):
            sam_file = os.path.join(sam_ldb_d, sam_file)
            if sam_file.endswith('.ldb'):
                logger.info('   backing up locked/related file ' + sam_file)
                copy_function(sam_file)
            else:
                logger.info('   copying locked/related file ' + sam_file)
                shutil.copyfile(sam_file, sam_file + self.backup_ext)

        if not mdb_backend:
            sam_obj.transaction_cancel()
Example #51
0
    def test_tdb_copy(self):
        src_ldb_file = os.path.join(self.tempdir, "source.ldb")
        dst_ldb_file = os.path.join(self.tempdir, "destination.ldb")

        # Create LDB source file with some content
        src_ldb = Ldb(src_ldb_file)
        src_ldb.add({"dn": "f=dc", "b": "bla"})

        # Copy source file to destination file and check return status
        self.assertIsNone(tdb_copy(src_ldb_file, dst_ldb_file))

        # Load copied file as LDB object
        dst_ldb = Ldb(dst_ldb_file)

        # Copmare contents of files
        self.assertEqual(
            src_ldb.searchone(basedn=ldb.Dn(src_ldb, "f=dc"), attribute="b"),
            dst_ldb.searchone(basedn=ldb.Dn(dst_ldb, "f=dc"), attribute="b")
        )

        # Clean up
        del src_ldb
        del dst_ldb
        os.unlink(src_ldb_file)
        os.unlink(dst_ldb_file)
Example #52
0
def ldif_to_samdb(dburl, lp, ldif_file, forced_local_dsa=None):
    """Routine to import all objects and attributes that are relevent
    to the KCC algorithms from a previously exported LDIF file.

    The point of this function is to allow a programmer/debugger to
    import an LDIF file with non-security relevent information that
    was previously extracted from a DC database.  The LDIF file is used
    to create a temporary abbreviated database.  The KCC algorithm can
    then run against this abbreviated database for debug or test
    verification that the topology generated is computationally the
    same between different OSes and algorithms.

    :param dburl: path to the temporary abbreviated db to create
    :param ldif_file: path to the ldif file to import
    """
    if os.path.exists(dburl):
        raise LdifError("Specify a database (%s) that doesn't already exist." %
                        dburl)

    # Use ["modules:"] as we are attempting to build a sam
    # database as opposed to start it here.
    tmpdb = Ldb(url=dburl, session_info=system_session(),
                lp=lp, options=["modules:"])

    tmpdb.transaction_start()
    try:
        data = read_and_sub_file(ldif_file, None)
        tmpdb.add_ldif(data, None)
        if forced_local_dsa:
            tmpdb.modify_ldif("""dn: @ROOTDSE
changetype: modify
replace: dsServiceName
dsServiceName: CN=NTDS Settings,%s
-
            """ % forced_local_dsa)

    except Exception, estr:
        tmpdb.transaction_cancel()
        raise LdifError("Failed to import %s: %s" % (ldif_file, estr))
Example #53
0
 def backup_secrets(self, private_dir, lp, logger):
     secrets_path = os.path.join(private_dir, 'secrets')
     secrets_obj = Ldb(secrets_path + '.ldb', lp=lp)
     logger.info('Starting transaction on ' + secrets_path)
     secrets_obj.transaction_start()
     self.offline_tdb_copy(secrets_path + '.ldb')
     self.offline_tdb_copy(secrets_path + '.tdb')
     secrets_obj.transaction_cancel()
Example #54
0
def accountcontrol(names, lp, creds, username=None, value=0):
    """enable/disable an OpenChange user account.

    :param lp: Loadparm context
    :param creds: Credentials context
    :param names: Provision Names object
    :param username: Name of user to disable
    :param value: the control value
    """
    db = Ldb(url=get_ldb_url(lp, creds, names), session_info=system_session(),
             credentials=creds, lp=lp)
    user_dn = get_user_dn(db, "CN=Users,%s" % names.domaindn, username)
    extended_user = """
dn: %s
changetype: modify
replace: msExchUserAccountControl
msExchUserAccountControl: %d
""" % (user_dn, value)
    db.modify_ldif(extended_user)
    if value == 2:
        print "[+] Account %s disabled" % username
    else:
        print "[+] Account %s enabled" % username
Example #55
0
    def post_setup(self):
        ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)

        # delete default SASL mappings
        res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
    
        # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
        for i in range (0, len(res)):
            dn = str(res[i]["dn"])
            ldapi_db.delete(dn)
            
        aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn
        
        m = ldb.Message()
        m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")

        m.dn = ldb.Dn(ldapi_db, self.names.domaindn)
        ldapi_db.modify(m)
            
        m.dn = ldb.Dn(ldapi_db, self.names.configdn)
        ldapi_db.modify(m)
            
        m.dn = ldb.Dn(ldapi_db, self.names.schemadn)
        ldapi_db.modify(m)
Example #56
0
    def __init__(self, setup_path, domain_sid, schemadn=None,
                 serverdn=None, files=None, prefixmap=None):
        """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
        
        :param samdb: Load a schema into a SamDB.
        :param setup_path: Setup path function.
        :param schemadn: DN of the schema
        :param serverdn: DN of the server
        
        Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
        """

        self.schemadn = schemadn
        self.ldb = Ldb()
        self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_R2_Attributes.txt'),
                                          setup_path('ad-schema/MS-AD_Schema_2K8_R2_Classes.txt'))

        if files is not None:
            for file in files:
                self.schema_data += open(file, 'r').read()

        self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
        check_all_substituted(self.schema_data)

        self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
                                                  {"SCHEMADN": schemadn,
                                                   "SERVERDN": serverdn,
                                                   })

        descr = b64encode(get_schema_descriptor(domain_sid))
        self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
                                               {"SCHEMADN": schemadn,
                                                "DESCRIPTOR": descr
                                                })

        self.prefixmap_data = open(setup_path("prefixMap.txt"), 'r').read()

        if prefixmap is not None:
            for map in prefixmap:
                self.prefixmap_data += "%s\n" % map

        self.prefixmap_data = b64encode(self.prefixmap_data)

        

        # We don't actually add this ldif, just parse it
        prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % self.prefixmap_data
        self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
Example #57
0
def delta_update_basesamdb(refsampath, sampath, creds, session, lp, message):
    """Update the provision container db: sam.ldb
    This function is aimed for alpha9 and newer;

    :param refsampath: Path to the samdb in the reference provision
    :param sampath: Path to the samdb in the upgraded provision
    :param creds: Credential used for openning LDB files
    :param session: Session to use for openning LDB files
    :param lp: A loadparam object
    :return: A msg_diff object with the difference between the @ATTRIBUTES
             of the current provision and the reference provision
    """

    message(SIMPLE,
            "Update base samdb by searching difference with reference one")
    refsam = Ldb(refsampath, session_info=session, credentials=creds,
                    lp=lp, options=["modules:"])
    sam = Ldb(sampath, session_info=session, credentials=creds, lp=lp,
                options=["modules:"])

    empty = ldb.Message()
    deltaattr = None
    reference = refsam.search(expression="")

    for refentry in reference:
        entry = sam.search(expression="distinguishedName=%s" % refentry["dn"],
                            scope=SCOPE_SUBTREE)
        if not len(entry):
            delta = sam.msg_diff(empty, refentry)
            message(CHANGE, "Adding %s to sam db" % str(refentry.dn))
            if str(refentry.dn) == "@PROVISION" and\
                delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
                delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
            delta.dn = refentry.dn
            sam.add(delta)
        else:
            delta = sam.msg_diff(entry[0], refentry)
            if str(refentry.dn) == "@ATTRIBUTES":
                deltaattr = sam.msg_diff(refentry, entry[0])
            if str(refentry.dn) == "@PROVISION" and\
                delta.get(samba.provision.LAST_PROVISION_USN_ATTRIBUTE):
                delta.remove(samba.provision.LAST_PROVISION_USN_ATTRIBUTE)
            if len(delta.items()) > 1:
                delta.dn = refentry.dn
                sam.modify(delta)

    return deltaattr
Example #58
0
class MapTestCase(MapBaseTestCase):

    def setUp(self):
        super(MapTestCase, self).setUp()
        ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
        ldb.set_opaque("skip_allocate_sids", "true");
        ldif = read_datafile("provision_samba3sam.ldif")
        ldb.add_ldif(self.samba4.subst(ldif))
        self.setup_modules(ldb, self.samba3, self.samba4)
        del ldb
        self.ldb = Ldb(self.ldburl, lp=self.lp, session_info=system_session())
        self.ldb.set_opaque("skip_allocate_sids", "true");

    def test_map_search(self):
        """Running search tests on mapped data."""
        self.samba3.db.add({
            "dn": "sambaDomainName=TESTS," + self.samba3.basedn,
            "objectclass": ["sambaDomain", "top"],
            "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739",
            "sambaNextRid": "2000",
            "sambaDomainName": "TESTS"
            })

        # Add a set of split records
        self.ldb.add_ldif("""
dn: """+ self.samba4.dn("cn=Domain Users") + """
objectClass: group
cn: Domain Users
objectSid: S-1-5-21-4231626423-2410014848-2360679739-513
""")

        # Add a set of split records
        self.ldb.add_ldif("""
dn: """+ self.samba4.dn("cn=X") + """
objectClass: user
cn: X
codePage: x
revision: x
dnsHostName: x
nextRid: y
lastLogon: x
description: x
objectSid: S-1-5-21-4231626423-2410014848-2360679739-1052
""")

        self.ldb.add({
            "dn": self.samba4.dn("cn=Y"),
            "objectClass": "top",
            "cn": "Y",
            "codePage": "x",
            "revision": "x",
            "dnsHostName": "y",
            "nextRid": "y",
            "lastLogon": "y",
            "description": "x"})

        self.ldb.add({
            "dn": self.samba4.dn("cn=Z"),
            "objectClass": "top",
            "cn": "Z",
            "codePage": "x",
            "revision": "y",
            "dnsHostName": "z",
            "nextRid": "y",
            "lastLogon": "z",
            "description": "y"})

        # Add a set of remote records

        self.samba3.db.add({
            "dn": self.samba3.dn("cn=A"),
            "objectClass": "posixAccount",
            "cn": "A",
            "sambaNextRid": "x",
            "sambaBadPasswordCount": "x",
            "sambaLogonTime": "x",
            "description": "x",
            "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-1052",
            "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"})

        self.samba3.db.add({
            "dn": self.samba3.dn("cn=B"),
            "objectClass": "top",
            "cn": "B",
            "sambaNextRid": "x",
            "sambaBadPasswordCount": "x",
            "sambaLogonTime": "y",
            "description": "x"})

        self.samba3.db.add({
            "dn": self.samba3.dn("cn=C"),
            "objectClass": "top",
            "cn": "C",
            "sambaNextRid": "x",
            "sambaBadPasswordCount": "y",
            "sambaLogonTime": "z",
            "description": "y"})

        # Testing search by DN

        # Search remote record by local DN
        dn = self.samba4.dn("cn=A")
        res = self.ldb.search(dn, scope=SCOPE_BASE,
                attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")

        # Search remote record by remote DN
        dn = self.samba3.dn("cn=A")
        res = self.samba3.db.search(dn, scope=SCOPE_BASE,
                attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertTrue(not "lastLogon" in res[0])
        self.assertEquals(str(res[0]["sambaLogonTime"]), "x")

        # Search split record by local DN
        dn = self.samba4.dn("cn=X")
        res = self.ldb.search(dn, scope=SCOPE_BASE,
                attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["dnsHostName"]), "x")
        self.assertEquals(str(res[0]["lastLogon"]), "x")

        # Search split record by remote DN
        dn = self.samba3.dn("cn=X")
        res = self.samba3.db.search(dn, scope=SCOPE_BASE,
                attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertTrue(not "lastLogon" in res[0])
        self.assertEquals(str(res[0]["sambaLogonTime"]), "x")

        # Testing search by attribute

        # Search by ignored attribute
        res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT,
                attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[0]["dnsHostName"]), "x")
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[1]["dnsHostName"]), "y")
        self.assertEquals(str(res[1]["lastLogon"]), "y")

        # Search by kept attribute
        res = self.ldb.search(expression="(description=y)",
                scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "z")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[1]["dnsHostName"]), "z")
        self.assertEquals(str(res[1]["lastLogon"]), "z")

        # Search by renamed attribute
        res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT,
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")

        # Search by converted attribute
        # TODO:
        #   Using the SID directly in the parse tree leads to conversion
        #   errors, letting the search fail with no results.
        #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-1052)", scope=SCOPE_DEFAULT, attrs)
        res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"])
        self.assertEquals(len(res), 4)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[1]["dnsHostName"]), "x")
        self.assertEquals(str(res[1]["lastLogon"]), "x")
        self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052",
                             res[1]["objectSid"])
        self.assertTrue("objectSid" in res[1])
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-1052",
                             res[0]["objectSid"])
        self.assertTrue("objectSid" in res[0])

        # Search by generated attribute
        # In most cases, this even works when the mapping is missing
        # a `convert_operator' by enumerating the remote db.
        res = self.ldb.search(expression="(primaryGroupID=512)",
                           attrs=["dnsHostName", "lastLogon", "primaryGroupID"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[0]["primaryGroupID"]), "512")

        # Note that Xs "objectSid" seems to be fine in the previous search for
        # "objectSid"...
        #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs)
        #print len(res) + " results found"
        #for i in range(len(res)):
        #    for (obj in res[i]) {
        #        print obj + ": " + res[i][obj]
        #    }
        #    print "---"
        #

        # Search by remote name of renamed attribute */
        res = self.ldb.search(expression="(sambaBadPasswordCount=*)",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 0)

        # Search by objectClass
        attrs = ["dnsHostName", "lastLogon", "objectClass"]
        res = self.ldb.search(expression="(objectClass=user)", attrs=attrs)
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[0]["objectClass"][0]), "user")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[1]["dnsHostName"]), "x")
        self.assertEquals(str(res[1]["lastLogon"]), "x")
        self.assertEquals(str(res[1]["objectClass"][0]), "user")

        # Prove that the objectClass is actually used for the search
        res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))",
                              attrs=attrs)
        self.assertEquals(len(res), 3)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(res[0]["objectClass"][0], "user")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(set(res[1]["objectClass"]), set(["top"]))
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[2]["dnsHostName"]), "x")
        self.assertEquals(str(res[2]["lastLogon"]), "x")
        self.assertEquals(str(res[2]["objectClass"][0]), "user")

        # Testing search by parse tree

        # Search by conjunction of local attributes
        res = self.ldb.search(expression="(&(codePage=x)(revision=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[0]["dnsHostName"]), "x")
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[1]["dnsHostName"]), "y")
        self.assertEquals(str(res[1]["lastLogon"]), "y")

        # Search by conjunction of remote attributes
        res = self.ldb.search(expression="(&(lastLogon=x)(description=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[1]["dnsHostName"]), "x")
        self.assertEquals(str(res[1]["lastLogon"]), "x")

        # Search by conjunction of local and remote attribute
        res = self.ldb.search(expression="(&(codePage=x)(description=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[0]["dnsHostName"]), "x")
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[1]["dnsHostName"]), "y")
        self.assertEquals(str(res[1]["lastLogon"]), "y")

        # Search by conjunction of local and remote attribute w/o match
        attrs = ["dnsHostName", "lastLogon"]
        res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))",
                              attrs=attrs)
        self.assertEquals(len(res), 0)
        res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))",
                              attrs=attrs)
        self.assertEquals(len(res), 0)

        # Search by disjunction of local attributes
        res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 2)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[0]["dnsHostName"]), "x")
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[1]["dnsHostName"]), "y")
        self.assertEquals(str(res[1]["lastLogon"]), "y")

        # Search by disjunction of remote attributes
        res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 3)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertFalse("dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertFalse("dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[2]["dnsHostName"]), "x")
        self.assertEquals(str(res[2]["lastLogon"]), "x")

        # Search by disjunction of local and remote attribute
        res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 3)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
        self.assertFalse("dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "y")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[1]["dnsHostName"]), "x")
        self.assertEquals(str(res[1]["lastLogon"]), "x")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[2]["dnsHostName"]), "y")
        self.assertEquals(str(res[2]["lastLogon"]), "y")

        # Search by disjunction of local and remote attribute w/o match
        res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 0)

        # Search by negated local attribute
        res = self.ldb.search(expression="(!(revision=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 6)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[2])
        self.assertEquals(str(res[2]["lastLogon"]), "z")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[3]["dnsHostName"]), "z")
        self.assertEquals(str(res[3]["lastLogon"]), "z")

        # Search by negated remote attribute
        res = self.ldb.search(expression="(!(description=x))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 4)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "z")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[1]["dnsHostName"]), "z")
        self.assertEquals(str(res[1]["lastLogon"]), "z")

        # Search by negated conjunction of local attributes
        res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 6)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[2])
        self.assertEquals(str(res[2]["lastLogon"]), "z")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[3]["dnsHostName"]), "z")
        self.assertEquals(str(res[3]["lastLogon"]), "z")

        # Search by negated conjunction of remote attributes
        res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 6)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "y")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "z")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[2]["dnsHostName"]), "y")
        self.assertEquals(str(res[2]["lastLogon"]), "y")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[3]["dnsHostName"]), "z")
        self.assertEquals(str(res[3]["lastLogon"]), "z")

        # Search by negated conjunction of local and remote attribute
        res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 6)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[2])
        self.assertEquals(str(res[2]["lastLogon"]), "z")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[3]["dnsHostName"]), "z")
        self.assertEquals(str(res[3]["lastLogon"]), "z")

        # Search by negated disjunction of local attributes
        res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))",
                              attrs=["dnsHostName", "lastLogon"])
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[2])
        self.assertEquals(str(res[2]["lastLogon"]), "z")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[3]["dnsHostName"]), "z")
        self.assertEquals(str(res[3]["lastLogon"]), "z")

        # Search by negated disjunction of remote attributes
        res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 5)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "z")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Y"))
        self.assertEquals(str(res[1]["dnsHostName"]), "y")
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[2]["dnsHostName"]), "z")
        self.assertEquals(str(res[2]["lastLogon"]), "z")

        # Search by negated disjunction of local and remote attribute
        res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))",
                              attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 5)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "z")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[2]["dnsHostName"]), "z")
        self.assertEquals(str(res[2]["lastLogon"]), "z")

        # Search by complex parse tree
        res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"])
        self.assertEquals(len(res), 7)
        res = sorted(res, key=attrgetter('dn'))
        self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
        self.assertTrue(not "dnsHostName" in res[0])
        self.assertEquals(str(res[0]["lastLogon"]), "x")
        self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
        self.assertTrue(not "dnsHostName" in res[1])
        self.assertEquals(str(res[1]["lastLogon"]), "y")
        self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
        self.assertTrue(not "dnsHostName" in res[2])
        self.assertEquals(str(res[2]["lastLogon"]), "z")
        self.assertEquals(str(res[3].dn), self.samba4.dn("cn=X"))
        self.assertEquals(str(res[3]["dnsHostName"]), "x")
        self.assertEquals(str(res[3]["lastLogon"]), "x")
        self.assertEquals(str(res[4].dn), self.samba4.dn("cn=Z"))
        self.assertEquals(str(res[4]["dnsHostName"]), "z")
        self.assertEquals(str(res[4]["lastLogon"]), "z")

        # Clean up
        dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]]
        for dn in dns:
            self.ldb.delete(dn)

    def test_map_modify_local(self):
        """Modification of local records."""
        # Add local record
        dn = "cn=test,dc=idealx,dc=org"
        self.ldb.add({"dn": dn,
                 "cn": "test",
                 "foo": "bar",
                 "revision": "1",
                 "description": "test"})
        # Check it's there
        attrs = ["foo", "revision", "description"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["foo"]), "bar")
        self.assertEquals(str(res[0]["revision"]), "1")
        self.assertEquals(str(res[0]["description"]), "test")
        # Check it's not in the local db
        res = self.samba4.db.search(expression="(cn=test)",
                                    scope=SCOPE_DEFAULT, attrs=attrs)
        self.assertEquals(len(res), 0)
        # Check it's not in the remote db
        res = self.samba3.db.search(expression="(cn=test)",
                                    scope=SCOPE_DEFAULT, attrs=attrs)
        self.assertEquals(len(res), 0)

        # Modify local record
        ldif = """
dn: """ + dn + """
replace: foo
foo: baz
replace: description
description: foo
"""
        self.ldb.modify_ldif(ldif)
        # Check in local db
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["foo"]), "baz")
        self.assertEquals(str(res[0]["revision"]), "1")
        self.assertEquals(str(res[0]["description"]), "foo")

        # Rename local record
        dn2 = "cn=toast,dc=idealx,dc=org"
        self.ldb.rename(dn, dn2)
        # Check in local db
        res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["foo"]), "baz")
        self.assertEquals(str(res[0]["revision"]), "1")
        self.assertEquals(str(res[0]["description"]), "foo")

        # Delete local record
        self.ldb.delete(dn2)
        # Check it's gone
        res = self.ldb.search(dn2, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)

    def test_map_modify_remote_remote(self):
        """Modification of remote data of remote records"""
        # Add remote record
        dn = self.samba4.dn("cn=test")
        dn2 = self.samba3.dn("cn=test")
        self.samba3.db.add({"dn": dn2,
                   "cn": "test",
                   "description": "foo",
                   "sambaBadPasswordCount": "3",
                   "sambaNextRid": "1001"})
        # Check it's there
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
                attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "foo")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
        # Check in mapped db
        attrs = ["description", "badPwdCount", "nextRid"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="")
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "foo")
        self.assertEquals(str(res[0]["badPwdCount"]), "3")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 0)

        # Modify remote data of remote record
        ldif = """
dn: """ + dn + """
replace: description
description: test
replace: badPwdCount
badPwdCount: 4
"""
        self.ldb.modify_ldif(ldif)
        # Check in mapped db
        res = self.ldb.search(dn, scope=SCOPE_BASE,
                attrs=["description", "badPwdCount", "nextRid"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["badPwdCount"]), "4")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        # Check in remote db
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
                attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")

        # Rename remote record
        dn2 = self.samba4.dn("cn=toast")
        self.ldb.rename(dn, dn2)
        # Check in mapped db
        dn = dn2
        res = self.ldb.search(dn, scope=SCOPE_BASE,
                attrs=["description", "badPwdCount", "nextRid"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["badPwdCount"]), "4")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        # Check in remote db
        dn2 = self.samba3.dn("cn=toast")
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
                attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")

        # Delete remote record
        self.ldb.delete(dn)
        # Check in mapped db that it's removed
        res = self.ldb.search(dn, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)
        # Check in remote db
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)

    def test_map_modify_remote_local(self):
        """Modification of local data of remote records"""
        # Add remote record (same as before)
        dn = self.samba4.dn("cn=test")
        dn2 = self.samba3.dn("cn=test")
        self.samba3.db.add({"dn": dn2,
                   "cn": "test",
                   "description": "foo",
                   "sambaBadPasswordCount": "3",
                   "sambaNextRid": "1001"})

        # Modify local data of remote record
        ldif = """
dn: """ + dn + """
add: revision
revision: 1
replace: description
description: test

"""
        self.ldb.modify_ldif(ldif)
        # Check in mapped db
        attrs = ["revision", "description"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["revision"]), "1")
        # Check in remote db
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertTrue(not "revision" in res[0])
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "description" in res[0])
        self.assertEquals(str(res[0]["revision"]), "1")

        # Delete (newly) split record
        self.ldb.delete(dn)

    def test_map_modify_split(self):
        """Testing modification of split records"""
        # Add split record
        dn = self.samba4.dn("cn=test")
        dn2 = self.samba3.dn("cn=test")
        self.ldb.add({
            "dn": dn,
            "cn": "test",
            "description": "foo",
            "badPwdCount": "3",
            "nextRid": "1001",
            "revision": "1"})
        # Check it's there
        attrs = ["description", "badPwdCount", "nextRid", "revision"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "foo")
        self.assertEquals(str(res[0]["badPwdCount"]), "3")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        self.assertEquals(str(res[0]["revision"]), "1")
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "description" in res[0])
        self.assertTrue(not "badPwdCount" in res[0])
        self.assertTrue(not "nextRid" in res[0])
        self.assertEquals(str(res[0]["revision"]), "1")
        # Check in remote db
        attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
                 "revision"]
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "foo")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
        self.assertTrue(not "revision" in res[0])

        # Modify of split record
        ldif = """
dn: """ + dn + """
replace: description
description: test
replace: badPwdCount
badPwdCount: 4
replace: revision
revision: 2
"""
        self.ldb.modify_ldif(ldif)
        # Check in mapped db
        attrs = ["description", "badPwdCount", "nextRid", "revision"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["badPwdCount"]), "4")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        self.assertEquals(str(res[0]["revision"]), "2")
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "description" in res[0])
        self.assertTrue(not "badPwdCount" in res[0])
        self.assertTrue(not "nextRid" in res[0])
        self.assertEquals(str(res[0]["revision"]), "2")
        # Check in remote db
        attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
                 "revision"]
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
        self.assertTrue(not "revision" in res[0])

        # Rename split record
        dn2 = self.samba4.dn("cn=toast")
        self.ldb.rename(dn, dn2)
        # Check in mapped db
        dn = dn2
        attrs = ["description", "badPwdCount", "nextRid", "revision"]
        res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["badPwdCount"]), "4")
        self.assertEquals(str(res[0]["nextRid"]), "1001")
        self.assertEquals(str(res[0]["revision"]), "2")
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn)
        self.assertTrue(not "description" in res[0])
        self.assertTrue(not "badPwdCount" in res[0])
        self.assertTrue(not "nextRid" in res[0])
        self.assertEquals(str(res[0]["revision"]), "2")
        # Check in remote db
        dn2 = self.samba3.dn("cn=toast")
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
          attrs=["description", "sambaBadPasswordCount", "sambaNextRid",
                 "revision"])
        self.assertEquals(len(res), 1)
        self.assertEquals(str(res[0].dn), dn2)
        self.assertEquals(str(res[0]["description"]), "test")
        self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
        self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
        self.assertTrue(not "revision" in res[0])

        # Delete split record
        self.ldb.delete(dn)
        # Check in mapped db
        res = self.ldb.search(dn, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)
        # Check in local db
        res = self.samba4.db.search(dn, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)
        # Check in remote db
        res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
        self.assertEquals(len(res), 0)