Exemple #1
0
class BaseDeleteTests(samba.tests.TestCase):
    def GUID_string(self, guid):
        return self.ldb.schema_format_value("objectGUID", guid)

    def setUp(self):
        super(BaseDeleteTests, self).setUp()
        self.ldb = SamDB(host,
                         credentials=creds,
                         session_info=system_session(lp),
                         lp=lp)

        self.base_dn = self.ldb.domain_dn()
        self.configuration_dn = self.ldb.get_config_basedn().get_linearized()

    def search_guid(self, guid):
        print("SEARCH by GUID %s" % self.GUID_string(guid))

        res = self.ldb.search(base="<GUID=%s>" % self.GUID_string(guid),
                              scope=SCOPE_BASE,
                              controls=["show_deleted:1"],
                              attrs=["*", "parentGUID"])
        self.assertEquals(len(res), 1)
        return res[0]

    def search_dn(self, dn):
        print("SEARCH by DN %s" % dn)

        res = self.ldb.search(expression="(objectClass=*)",
                              base=dn,
                              scope=SCOPE_BASE,
                              controls=["show_deleted:1"],
                              attrs=["*", "parentGUID"])
        self.assertEquals(len(res), 1)
        return res[0]
Exemple #2
0
class DirsyncBaseTests(samba.tests.TestCase):
    def setUp(self):
        super(DirsyncBaseTests, self).setUp()
        self.ldb_admin = SamDB(ldapshost,
                               credentials=creds,
                               session_info=system_session(lp),
                               lp=lp)
        self.base_dn = self.ldb_admin.domain_dn()
        self.domain_sid = security.dom_sid(self.ldb_admin.get_domain_sid())
        self.user_pass = samba.generate_random_password(12, 16)
        self.configuration_dn = self.ldb_admin.get_config_basedn(
        ).get_linearized()
        self.sd_utils = sd_utils.SDUtils(self.ldb_admin)
        #used for anonymous login
        print("baseDN: %s" % self.base_dn)

    def get_user_dn(self, name):
        return "CN=%s,CN=Users,%s" % (name, self.base_dn)

    def get_ldb_connection(self, target_username, target_password):
        creds_tmp = Credentials()
        creds_tmp.set_username(target_username)
        creds_tmp.set_password(target_password)
        creds_tmp.set_domain(creds.get_domain())
        creds_tmp.set_realm(creds.get_realm())
        creds_tmp.set_workstation(creds.get_workstation())
        creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
                                      | gensec.FEATURE_SEAL)
        creds_tmp.set_kerberos_state(
            DONT_USE_KERBEROS)  # kinit is too expensive to use in a tight loop
        ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp)
        return ldb_target
Exemple #3
0
class DirsyncBaseTests(samba.tests.TestCase):

    def setUp(self):
        super(DirsyncBaseTests, self).setUp()
        self.ldb_admin = SamDB(ldapshost, credentials=creds, session_info=system_session(lp), lp=lp)
        self.base_dn = self.ldb_admin.domain_dn()
        self.domain_sid = security.dom_sid(self.ldb_admin.get_domain_sid())
        self.user_pass = samba.generate_random_password(12, 16)
        self.configuration_dn = self.ldb_admin.get_config_basedn().get_linearized()
        self.sd_utils = sd_utils.SDUtils(self.ldb_admin)
        #used for anonymous login
        print("baseDN: %s" % self.base_dn)

    def get_user_dn(self, name):
        return "CN=%s,CN=Users,%s" % (name, self.base_dn)

    def get_ldb_connection(self, target_username, target_password):
        creds_tmp = Credentials()
        creds_tmp.set_username(target_username)
        creds_tmp.set_password(target_password)
        creds_tmp.set_domain(creds.get_domain())
        creds_tmp.set_realm(creds.get_realm())
        creds_tmp.set_workstation(creds.get_workstation())
        creds_tmp.set_gensec_features(creds_tmp.get_gensec_features()
                                      | gensec.FEATURE_SEAL)
        creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop
        ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp)
        return ldb_target
Exemple #4
0
    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H, session_info=system_session(),
            credentials=creds, lp=lp)

        domain_dn = samdb.domain_dn()
        forest_dn = samba.dn_from_dns_name(samdb.forest_dns_name())
        infrastructure_dn = "CN=Infrastructure," + domain_dn
        naming_dn = "CN=Partitions,%s" % samdb.get_config_basedn()
        schema_dn = samdb.get_schema_basedn()
        rid_dn = "CN=RID Manager$,CN=System," + domain_dn
        domaindns_dn = "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
        forestdns_dn = "CN=Infrastructure,DC=ForestDnsZones," + forest_dn

        masters = [(schema_dn, "schema", "SchemaMasterRole"),
                   (infrastructure_dn, "infrastructure", "InfrastructureMasterRole"),
                   (rid_dn, "rid", "RidAllocationMasterRole"),
                   (domain_dn, "pdc", "PdcEmulationMasterRole"),
                   (naming_dn, "naming", "DomainNamingMasterRole"),
                   (domaindns_dn, "domaindns", "DomainDnsZonesMasterRole"),
                   (forestdns_dn, "forestdns", "ForestDnsZonesMasterRole"),
        ]

        for master in masters:
            (dn, short_name, long_name) = master
            try:
                master = get_fsmo_roleowner(samdb, dn, short_name)
                if master is not None:
                    self.message("%s owner: %s" % (long_name, str(master)))
                else:
                    self.message("%s has no current owner" % (long_name))
            except CommandError, e:
                self.message("%s: * %s" % (long_name, e.message))
Exemple #5
0
    def run(self,
            subnetname,
            site_of_subnet,
            H=None,
            sambaopts=None,
            credopts=None,
            versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        samdb.transaction_start()
        try:
            subnets.create_subnet(samdb, samdb.get_config_basedn(), subnetname,
                                  site_of_subnet)
            samdb.transaction_commit()
        except subnets.SubnetException as e:
            samdb.transaction_cancel()
            raise CommandError("Error while creating subnet %s: %s" %
                               (subnetname, e))

        self.outf.write("Subnet %s created !\n" % subnetname)
Exemple #6
0
    def run(self,
            subnetname,
            site_of_subnet,
            H=None,
            sambaopts=None,
            credopts=None,
            versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        samdb.transaction_start()
        try:
            subnets.set_subnet_site(samdb, samdb.get_config_basedn(),
                                    subnetname, site_of_subnet)
            samdb.transaction_commit()
        except subnets.SubnetException as e:
            samdb.transaction_cancel()
            raise CommandError("Error assigning subnet %s to site %s: %s" %
                               (subnetname, site_of_subnet, e))

        print(("Subnet %s shifted to site %s" % (subnetname, site_of_subnet)),
              file=self.outf)
Exemple #7
0
class BaseDeleteTests(samba.tests.TestCase):

    def GUID_string(self, guid):
        return self.ldb.schema_format_value("objectGUID", guid)

    def setUp(self):
        super(BaseDeleteTests, self).setUp()
        self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp)

        self.base_dn = self.ldb.domain_dn()
        self.configuration_dn = self.ldb.get_config_basedn().get_linearized()

    def search_guid(self, guid):
        print "SEARCH by GUID {0!s}".format(self.GUID_string(guid))

        res = self.ldb.search(base="<GUID={0!s}>".format(self.GUID_string(guid)),
                         scope=SCOPE_BASE, controls=["show_deleted:1"])
        self.assertEquals(len(res), 1)
        return res[0]

    def search_dn(self,dn):
        print "SEARCH by DN {0!s}".format(dn)

        res = self.ldb.search(expression="(objectClass=*)",
                         base=dn,
                         scope=SCOPE_BASE,
                         controls=["show_deleted:1"])
        self.assertEquals(len(res), 1)
        return res[0]
Exemple #8
0
    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H, session_info=system_session(),
            credentials=creds, lp=lp)

        domain_dn = samdb.domain_dn()
        forest_dn = samba.dn_from_dns_name(samdb.forest_dns_name())
        infrastructure_dn = "CN=Infrastructure," + domain_dn
        naming_dn = "CN=Partitions,%s" % samdb.get_config_basedn()
        schema_dn = samdb.get_schema_basedn()
        rid_dn = "CN=RID Manager$,CN=System," + domain_dn
        domaindns_dn = "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
        forestdns_dn = "CN=Infrastructure,DC=ForestDnsZones," + forest_dn

        infrastructureMaster = get_fsmo_roleowner(samdb, infrastructure_dn)
        pdcEmulator = get_fsmo_roleowner(samdb, domain_dn)
        namingMaster = get_fsmo_roleowner(samdb, naming_dn)
        schemaMaster = get_fsmo_roleowner(samdb, schema_dn)
        ridMaster = get_fsmo_roleowner(samdb, rid_dn)
        domaindnszonesMaster = get_fsmo_roleowner(samdb, domaindns_dn)
        forestdnszonesMaster = get_fsmo_roleowner(samdb, forestdns_dn)

        self.message("SchemaMasterRole owner: " + schemaMaster)
        self.message("InfrastructureMasterRole owner: " + infrastructureMaster)
        self.message("RidAllocationMasterRole owner: " + ridMaster)
        self.message("PdcEmulationMasterRole owner: " + pdcEmulator)
        self.message("DomainNamingMasterRole owner: " + namingMaster)
        self.message("DomainDnsZonesMasterRole owner: " + domaindnszonesMaster)
        self.message("ForestDnsZonesMasterRole owner: " + forestdnszonesMaster)
Exemple #9
0
    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        domain_dn = samdb.domain_dn()
        forest_dn = samba.dn_from_dns_name(samdb.forest_dns_name())
        infrastructure_dn = "CN=Infrastructure," + domain_dn
        naming_dn = "CN=Partitions,%s" % samdb.get_config_basedn()
        schema_dn = samdb.get_schema_basedn()
        rid_dn = "CN=RID Manager$,CN=System," + domain_dn
        domaindns_dn = "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
        forestdns_dn = "CN=Infrastructure,DC=ForestDnsZones," + forest_dn

        infrastructureMaster = get_fsmo_roleowner(samdb, infrastructure_dn)
        pdcEmulator = get_fsmo_roleowner(samdb, domain_dn)
        namingMaster = get_fsmo_roleowner(samdb, naming_dn)
        schemaMaster = get_fsmo_roleowner(samdb, schema_dn)
        ridMaster = get_fsmo_roleowner(samdb, rid_dn)
        domaindnszonesMaster = get_fsmo_roleowner(samdb, domaindns_dn)
        forestdnszonesMaster = get_fsmo_roleowner(samdb, forestdns_dn)

        self.message("SchemaMasterRole owner: " + schemaMaster)
        self.message("InfrastructureMasterRole owner: " + infrastructureMaster)
        self.message("RidAllocationMasterRole owner: " + ridMaster)
        self.message("PdcEmulationMasterRole owner: " + pdcEmulator)
        self.message("DomainNamingMasterRole owner: " + namingMaster)
        self.message("DomainDnsZonesMasterRole owner: " + domaindnszonesMaster)
        self.message("ForestDnsZonesMasterRole owner: " + forestdnszonesMaster)
    def check_restored_database(self, bkp_lp, expect_secrets=True):
        paths = provision.provision_paths_from_lp(bkp_lp, bkp_lp.get("realm"))

        bkp_pd = get_prim_dom(paths.secrets, bkp_lp)
        self.assertEqual(len(bkp_pd), 1)
        acn = bkp_pd[0].get('samAccountName')
        self.assertIsNotNone(acn)
        self.assertEqual(str(acn[0]), self.new_server + '$')
        self.assertIsNotNone(bkp_pd[0].get('secret'))

        samdb = SamDB(url=paths.samdb,
                      session_info=system_session(),
                      lp=bkp_lp,
                      credentials=self.get_credentials())

        # check that the backup markers have been removed from the restored DB
        res = samdb.search(base=ldb.Dn(samdb, "@SAMBA_DSDB"),
                           scope=ldb.SCOPE_BASE,
                           attrs=self.backup_markers)
        self.assertEqual(len(res), 1)
        for marker in self.backup_markers:
            self.assertIsNone(res[0].get(marker),
                              "%s backup-marker left behind" % marker)

        # check that the repsFrom and repsTo values have been removed
        # from the restored DB
        res = samdb.search(base=samdb.get_default_basedn(),
                           scope=ldb.SCOPE_BASE,
                           attrs=['repsFrom', 'repsTo'])
        self.assertEqual(len(res), 1)
        self.assertIsNone(res[0].get('repsFrom'))
        self.assertIsNone(res[0].get('repsTo'))

        res = samdb.search(base=samdb.get_config_basedn(),
                           scope=ldb.SCOPE_BASE,
                           attrs=['repsFrom', 'repsTo'])
        self.assertEqual(len(res), 1)
        self.assertIsNone(res[0].get('repsFrom'))
        self.assertIsNone(res[0].get('repsTo'))

        # check the DB is using the backend we supplied
        if self.backend:
            res = samdb.search(base="@PARTITION",
                               scope=ldb.SCOPE_BASE,
                               attrs=["backendStore"])
            backend = str(res[0].get("backendStore"))
            self.assertEqual(backend, self.backend)

        # check the restored DB has the expected partitions/DC/FSMO roles
        self.assert_partitions_present(samdb)
        self.assert_dcs_present(samdb, self.new_server, expected_count=1)
        self.assert_fsmo_roles(samdb, self.new_server, self.server)
        self.assert_secrets(samdb, expect_secrets=expect_secrets)

        # check we still have an uptodateness vector for the original DC
        self.assert_repl_uptodate_vector(samdb)
        return samdb
Exemple #11
0
def deprovision(setup_path, lp, creds, firstorg=None, firstou=None, reporter=None):
    """Remove all configuration entries added by the OpenChange
    installation.

    :param setup_path: Path to the setup directory.
    :param names: provision names object.
    :param lp: Loadparm context
    :param creds: Credentials Context
    :param reporter: A progress reporter instance (subclass of AbstractProgressReporter)
    """
    if reporter is None:
        reporter = TextProgressReporter()

    session_info = system_session()

    lp.set("dsdb:schema update allowed", "yes")

    names = guess_names_from_smbconf(lp, None, None)

    samdb = SamDB(url=get_ldb_url(lp, creds, names), session_info=session_info,
                  credentials=creds, lp=lp)

    try:
        config_dn = samdb.get_config_basedn()
        ret = samdb.search(base=config_dn, scope=ldb.SCOPE_SUBTREE, expression="(objectClass=msExchExchangeServer)")
        if len(ret) > 1:
            # If we are the primary folder store server, raise exception
            # The user has to set another server as primary before unregister
            # this one
            our_siteFolderName = "CN=Public Folder Store (%s),CN=First Storage Group,CN=InformationStore,CN=%s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,%s" % (names.netbiosname, names.netbiosname, names.firstorgdn)
            dn = "CN=First Administrative Group,CN=Administrative Groups,%s" % names.firstorgdn
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['siteFolderServer'])
            assert len(ret) == 1
            siteFolderName = ret[0]["siteFolderServer"][0]
            if our_siteFolderName.lower() == siteFolderName.lower():
                raise Exception("This server is the primary folder store server")

            # If we are the primary receipt update service, raise exception
            our_addressListServiceLink = "CN=%s,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,%s" % (names.netbiosname, names.firstorgdn)
            dn = "CN=Recipient Update Service (%s),CN=Recipient Update Services,CN=Address Lists Container,%s" % (names.domain, names.firstorgdn)
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['msExchAddressListServiceLink'])
            assert len(ret) == 1
            addressListServiceLink = ret[0]['msExchAddressListServiceLink'][0]
            if our_addressListServiceLink.lower() == addressListServiceLink.lower():
                raise Exception("This server is the primary receipt update service server")

            # Unregister the server
            deprovision_schema(setup_path, names, lp, creds, reporter, "AD/oc_provision_configuration_new_server.ldif", "Remove Exchange samba registration")
        else:
            # This is the unique server, remove full schema
            deprovision_schema(setup_path, names, lp, creds, reporter, "AD/oc_provision_configuration.ldif", "Remove Exchange configuration objects")
    except LdbError, ldb_error:
        print ("[!] error while deprovisioning the Exchange configuration"
               " objects (%d): %s" % ldb_error.args)
Exemple #12
0
    def run(self, subnetname, H=None, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            subnets.delete_subnet(samdb, samdb.get_config_basedn(), subnetname)
            samdb.transaction_commit()
        except subnets.SubnetException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while removing subnet %s, error: %s" % (subnetname, e))
Exemple #13
0
    def run(self, subnetname, site_of_subnet, H=None, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            subnets.set_subnet_site(samdb, samdb.get_config_basedn(), subnetname, site_of_subnet)
            samdb.transaction_commit()
        except subnets.SubnetException, e:
            samdb.transaction_cancel()
            raise CommandError("Error assigning subnet %s to site %s: %s" % (subnetname, site_of_subnet, e))
Exemple #14
0
    def run(self, sitename, H=None, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            sites.delete_site(samdb, samdb.get_config_basedn(), sitename)
            samdb.transaction_commit()
        except sites.SiteException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while removing site %s, error: %s" % (sitename, str(e)))
Exemple #15
0
class SitesBaseTests(samba.tests.TestCase):

    def setUp(self):
        super(SitesBaseTests, self).setUp()
        self.ldb = SamDB(ldaphost, credentials=creds,
                         session_info=system_session(lp), lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
        self.configuration_dn = self.ldb.get_config_basedn().get_linearized()

    def get_user_dn(self, name):
        return "CN=%s,CN=Users,%s" % (name, self.base_dn)
Exemple #16
0
class SitesBaseTests(samba.tests.TestCase):

    def setUp(self):
        super(SitesBaseTests, self).setUp()
        self.ldb = SamDB(ldaphost, credentials=creds,
                         session_info=system_session(lp), lp=lp)
        self.base_dn = self.ldb.domain_dn()
        self.domain_sid = security.dom_sid(self.ldb.get_domain_sid())
        self.configuration_dn = self.ldb.get_config_basedn().get_linearized()

    def get_user_dn(self, name):
        return "CN={0!s},CN=Users,{1!s}".format(name, self.base_dn)
Exemple #17
0
    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        domain_dn = samdb.domain_dn()
        self.infrastructure_dn = "CN=Infrastructure," + domain_dn
        self.naming_dn = "CN=Partitions,%s" % samdb.get_config_basedn()
        self.schema_dn = samdb.get_schema_basedn()
        self.rid_dn = "CN=RID Manager$,CN=System," + domain_dn

        res = samdb.search(self.infrastructure_dn,
                           scope=ldb.SCOPE_BASE,
                           attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.infrastructureMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(domain_dn,
                           scope=ldb.SCOPE_BASE,
                           attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.pdcEmulator = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.naming_dn,
                           scope=ldb.SCOPE_BASE,
                           attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.namingMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.schema_dn,
                           scope=ldb.SCOPE_BASE,
                           attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.schemaMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.rid_dn,
                           scope=ldb.SCOPE_BASE,
                           attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.ridMaster = res[0]["fSMORoleOwner"][0]

        self.message("InfrastructureMasterRole owner: " +
                     self.infrastructureMaster)
        self.message("RidAllocationMasterRole owner: " + self.ridMaster)
        self.message("PdcEmulationMasterRole owner: " + self.pdcEmulator)
        self.message("DomainNamingMasterRole owner: " + self.namingMaster)
        self.message("SchemaMasterRole owner: " + self.schemaMaster)
Exemple #18
0
    def run(self, subnetname, site_of_subnet, H=None, sambaopts=None,
            credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H, session_info=system_session(),
                      credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            subnets.create_subnet(samdb, samdb.get_config_basedn(), subnetname,
                                  site_of_subnet)
            samdb.transaction_commit()
        except subnets.SubnetException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while creating subnet {0!s}: {1!s}".format(subnetname, e))
Exemple #19
0
    def check_restored_database(self, bkp_lp, expect_secrets=True):
        paths = provision.provision_paths_from_lp(bkp_lp, bkp_lp.get("realm"))

        bkp_pd = get_prim_dom(paths.secrets, bkp_lp)
        self.assertEqual(len(bkp_pd), 1)
        acn = bkp_pd[0].get('samAccountName')
        self.assertIsNotNone(acn)
        self.assertEqual(acn[0].replace('$', ''), self.new_server)
        self.assertIsNotNone(bkp_pd[0].get('secret'))

        samdb = SamDB(url=paths.samdb,
                      session_info=system_session(),
                      lp=bkp_lp,
                      credentials=self.get_credentials())

        # check that the backup markers have been removed from the restored DB
        res = samdb.search(base=ldb.Dn(samdb, "@SAMBA_DSDB"),
                           scope=ldb.SCOPE_BASE,
                           attrs=self.backup_markers)
        self.assertEqual(len(res), 1)
        for marker in self.backup_markers:
            self.assertIsNone(res[0].get(marker),
                              "%s backup-marker left behind" % marker)

        # check that the repsFrom and repsTo values have been removed
        # from the restored DB
        res = samdb.search(base=samdb.get_default_basedn(),
                           scope=ldb.SCOPE_BASE,
                           attrs=['repsFrom', 'repsTo'])
        self.assertEqual(len(res), 1)
        self.assertIsNone(res[0].get('repsFrom'))
        self.assertIsNone(res[0].get('repsTo'))

        res = samdb.search(base=samdb.get_config_basedn(),
                           scope=ldb.SCOPE_BASE,
                           attrs=['repsFrom', 'repsTo'])
        self.assertEqual(len(res), 1)
        self.assertIsNone(res[0].get('repsFrom'))
        self.assertIsNone(res[0].get('repsTo'))

        # check the restored DB has the expected partitions/DC/FSMO roles
        self.assert_partitions_present(samdb)
        self.assert_dcs_present(samdb, self.new_server, expected_count=1)
        self.assert_fsmo_roles(samdb, self.new_server, self.server)
        self.assert_secrets(samdb, expect_secrets=expect_secrets)
        return samdb
Exemple #20
0
    def run(self, sitename, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        url =  lp.private_path("sam.ldb")

        if not os.path.exists(url):
            raise CommandError("secret database not found at %s " % url)
        samdb = SamDB(url=url, session_info=system_session(),
            credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            ok = sites.delete_site(samdb, samdb.get_config_basedn(), sitename)
            samdb.transaction_commit()
        except sites.SiteException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while removing site %s, error: %s" % (sitename, str(e)))
Exemple #21
0
    def run(self, sitename, H=None, sambaopts=None, credopts=None,
            versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        samdb = SamDB(url=H, session_info=system_session(),
                      credentials=creds, lp=lp)

        samdb.transaction_start()
        try:
            sites.delete_site(samdb, samdb.get_config_basedn(), sitename)
            samdb.transaction_commit()
        except sites.SiteException as e:
            samdb.transaction_cancel()
            raise CommandError(
                "Error while removing site %s, error: %s" % (sitename, str(e)))

        self.outf.write("Site %s removed!\n" % sitename)
Exemple #22
0
    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H, session_info=system_session(),
            credentials=creds, lp=lp)

        domain_dn = samdb.domain_dn()
        self.infrastructure_dn = "CN=Infrastructure," + domain_dn
        self.naming_dn = "CN=Partitions,%s" % samdb.get_config_basedn()
        self.schema_dn = samdb.get_schema_basedn()
        self.rid_dn = "CN=RID Manager$,CN=System," + domain_dn

        res = samdb.search(self.infrastructure_dn,
                           scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.infrastructureMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(domain_dn,
                           scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.pdcEmulator = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.naming_dn,
                           scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.namingMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.schema_dn,
                           scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.schemaMaster = res[0]["fSMORoleOwner"][0]

        res = samdb.search(self.rid_dn,
                           scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
        assert len(res) == 1
        self.ridMaster = res[0]["fSMORoleOwner"][0]

        self.message("InfrastructureMasterRole owner: " + self.infrastructureMaster)
        self.message("RidAllocationMasterRole owner: " + self.ridMaster)
        self.message("PdcEmulationMasterRole owner: " + self.pdcEmulator)
        self.message("DomainNamingMasterRole owner: " + self.namingMaster)
        self.message("SchemaMasterRole owner: " + self.schemaMaster)
Exemple #23
0
    def run(self, sitename, sambaopts=None, credopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)
        url = lp.private_path("sam.ldb")

        if not os.path.exists(url):
            raise CommandError("secret database not found at %s " % url)
        samdb = SamDB(url=url,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        samdb.transaction_start()
        try:
            ok = sites.delete_site(samdb, samdb.get_config_basedn(), sitename)
            samdb.transaction_commit()
        except sites.SiteException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while removing site %s, error: %s" %
                               (sitename, str(e)))
Exemple #24
0
    def run(self,
            subnetname,
            H=None,
            sambaopts=None,
            credopts=None,
            versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        samdb.transaction_start()
        try:
            subnets.delete_subnet(samdb, samdb.get_config_basedn(), subnetname)
            samdb.transaction_commit()
        except subnets.SubnetException, e:
            samdb.transaction_cancel()
            raise CommandError("Error while removing subnet %s, error: %s" %
                               (subnetname, e))
Exemple #25
0
class PassWordHashTests(TestCase):

    def setUp(self):
        self.lp = samba.tests.env_loadparm()
        super(PassWordHashTests, self).setUp()

    def set_store_cleartext(self, cleartext):
        # get the current pwdProperties
        pwdProperties = self.ldb.get_pwdProperties()
        # update the clear-text properties flag
        props = int(pwdProperties)
        if cleartext:
            props |= DOMAIN_PASSWORD_STORE_CLEARTEXT
        else:
            props &= ~DOMAIN_PASSWORD_STORE_CLEARTEXT
        self.ldb.set_pwdProperties(str(props))

    # Add a user to ldb, this will exercise the password_hash code
    # and calculate the appropriate supplemental credentials
    def add_user(self, options=None, clear_text=False, ldb=None):
        # set any needed options
        if options is not None:
            for (option, value) in options:
                self.lp.set(option, value)

        if ldb is None:
            self.creds = Credentials()
            self.session = system_session()
            self.creds.guess(self.lp)
            self.session = system_session()
            self.ldb = SamDB(session_info=self.session,
                             credentials=self.creds,
                             lp=self.lp)
        else:
            self.ldb = ldb

        res = self.ldb.search(base=self.ldb.get_config_basedn(),
                              expression="ncName=%s" % self.ldb.get_default_basedn(),
                              attrs=["nETBIOSName"])
        self.netbios_domain = res[0]["nETBIOSName"][0]
        self.dns_domain = self.ldb.domain_dns_name()


        # Gets back the basedn
        base_dn = self.ldb.domain_dn()

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # permit password changes during this test
        PasswordCommon.allow_password_changes(self, self.ldb)

        self.base_dn = self.ldb.domain_dn()

        account_control = 0
        if clear_text:
            # Restore the current domain setting on exit.
            pwdProperties = self.ldb.get_pwdProperties()
            self.addCleanup(self.ldb.set_pwdProperties, pwdProperties)
            # Update the domain setting
            self.set_store_cleartext(clear_text)
            account_control |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED

        # (Re)adds the test user USER_NAME with password USER_PASS
        # and userPrincipalName UPN
        delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": USER_NAME,
             "userPassword": USER_PASS,
             "userPrincipalName": UPN,
             "userAccountControl": str(account_control)
        })

    # Get the supplemental credentials for the user under test
    def get_supplemental_creds(self):
        base = "cn=" + USER_NAME + ",cn=users," + self.base_dn
        res = self.ldb.search(scope=ldb.SCOPE_BASE,
                              base=base,
                              attrs=["supplementalCredentials"])
        self.assertIs(True, len(res) > 0)
        obj = res[0]
        sc_blob = obj["supplementalCredentials"][0]
        sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob)
        return sc

    # Calculate and validate a Wdigest value
    def check_digest(self, user, realm, password,  digest):
        expected = calc_digest(user, realm, password)
        actual = binascii.hexlify(bytearray(digest))
        error = "Digest expected[%s], actual[%s], " \
                "user[%s], realm[%s], pass[%s]" % \
                (expected, actual, user, realm, password)
        self.assertEquals(expected, actual, error)

    # Check all of the 29 expected WDigest values
    #
    def check_wdigests(self, digests):

        self.assertEquals(29, digests.num_hashes)

        # Using the n-1 pattern in the array indexes to make it easier
        # to check the tests against the spec and the samba-tool user tests.
        self.check_digest(USER_NAME,
                          self.netbios_domain,
                          USER_PASS,
                          digests.hashes[1-1].hash)
        self.check_digest(USER_NAME.lower(),
                          self.netbios_domain.lower(),
                          USER_PASS,
                          digests.hashes[2-1].hash)
        self.check_digest(USER_NAME.upper(),
                          self.netbios_domain.upper(),
                          USER_PASS,
                          digests.hashes[3-1].hash)
        self.check_digest(USER_NAME,
                          self.netbios_domain.upper(),
                          USER_PASS,
                          digests.hashes[4-1].hash)
        self.check_digest(USER_NAME,
                          self.netbios_domain.lower(),
                          USER_PASS,
                          digests.hashes[5-1].hash)
        self.check_digest(USER_NAME.upper(),
                          self.netbios_domain.lower(),
                          USER_PASS,
                          digests.hashes[6-1].hash)
        self.check_digest(USER_NAME.lower(),
                          self.netbios_domain.upper(),
                          USER_PASS,
                          digests.hashes[7-1].hash)
        self.check_digest(USER_NAME,
                          self.dns_domain,
                          USER_PASS,
                          digests.hashes[8-1].hash)
        self.check_digest(USER_NAME.lower(),
                          self.dns_domain.lower(),
                          USER_PASS,
                          digests.hashes[9-1].hash)
        self.check_digest(USER_NAME.upper(),
                          self.dns_domain.upper(),
                          USER_PASS,
                          digests.hashes[10-1].hash)
        self.check_digest(USER_NAME,
                          self.dns_domain.upper(),
                          USER_PASS,
                          digests.hashes[11-1].hash)
        self.check_digest(USER_NAME,
                          self.dns_domain.lower(),
                          USER_PASS,
                          digests.hashes[12-1].hash)
        self.check_digest(USER_NAME.upper(),
                          self.dns_domain.lower(),
                          USER_PASS,
                          digests.hashes[13-1].hash)
        self.check_digest(USER_NAME.lower(),
                          self.dns_domain.upper(),
                          USER_PASS,
                          digests.hashes[14-1].hash)
        self.check_digest(UPN,
                          "",
                          USER_PASS,
                          digests.hashes[15-1].hash)
        self.check_digest(UPN.lower(),
                          "",
                          USER_PASS,
                          digests.hashes[16-1].hash)
        self.check_digest(UPN.upper(),
                          "",
                          USER_PASS,
                          digests.hashes[17-1].hash)

        name = "%s\\%s" % (self.netbios_domain, USER_NAME)
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[18-1].hash)

        name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[19-1].hash)

        name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[20-1].hash)
        self.check_digest(USER_NAME,
                          "Digest",
                          USER_PASS,
                          digests.hashes[21-1].hash)
        self.check_digest(USER_NAME.lower(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[22-1].hash)
        self.check_digest(USER_NAME.upper(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[23-1].hash)
        self.check_digest(UPN,
                          "Digest",
                          USER_PASS,
                          digests.hashes[24-1].hash)
        self.check_digest(UPN.lower(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[25-1].hash)
        self.check_digest(UPN.upper(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[26-1].hash)
        name = "%s\\%s" % (self.netbios_domain, USER_NAME)
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[27-1].hash)

        name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[28-1].hash)

        name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[29-1].hash)

    def checkUserPassword(self, up, expected):

        # Check we've received the correct number of hashes
        self.assertEquals(len(expected), up.num_hashes)

        i = 0
        for (tag, alg, rounds) in expected:
            self.assertEquals(tag, up.hashes[i].scheme)

            data = up.hashes[i].value.split("$")
            # Check we got the expected crypt algorithm
            self.assertEquals(alg, data[1])

            if rounds is None:
                cmd = "$%s$%s" % (alg, data[2])
            else:
                cmd = "$%s$rounds=%d$%s" % (alg, rounds, data[3])

            # Calculate the expected hash value
            expected = crypt.crypt(USER_PASS, cmd)
            self.assertEquals(expected, up.hashes[i].value)
            i += 1

    # Check that the correct nt_hash was stored for userPassword
    def checkNtHash(self, password, nt_hash):
        creds = Credentials()
        creds.set_anonymous()
        creds.set_password(password)
        expected = creds.get_nt_hash()
        actual = bytearray(nt_hash)
        self.assertEquals(expected, actual)
Exemple #26
0
    def run(self,
            sambaopts=None,
            credopts=None,
            backup_file=None,
            targetdir=None,
            newservername=None,
            host_ip=None,
            host_ip6=None):
        if not (backup_file and os.path.exists(backup_file)):
            raise CommandError('Backup file not found.')
        if targetdir is None:
            raise CommandError('Please specify a target directory')
        # allow restoredc to install into a directory prepopulated by selftest
        if (os.path.exists(targetdir) and os.listdir(targetdir)
                and os.environ.get('SAMBA_SELFTEST') != '1'):
            raise CommandError('Target directory is not empty')
        if not newservername:
            raise CommandError('Server name required')

        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        logger.addHandler(logging.StreamHandler(sys.stdout))

        # ldapcmp prefers the server's netBIOS name in upper-case
        newservername = newservername.upper()

        # extract the backup .tar to a temp directory
        targetdir = os.path.abspath(targetdir)
        tf = tarfile.open(backup_file)
        tf.extractall(targetdir)
        tf.close()

        # use the smb.conf that got backed up, by default (save what was
        # actually backed up, before we mess with it)
        smbconf = os.path.join(targetdir, 'etc', 'smb.conf')
        shutil.copyfile(smbconf, smbconf + ".orig")

        # if a smb.conf was specified on the cmd line, then use that instead
        cli_smbconf = sambaopts.get_loadparm_path()
        if cli_smbconf:
            logger.info("Using %s as restored domain's smb.conf" % cli_smbconf)
            shutil.copyfile(cli_smbconf, smbconf)

        lp = samba.param.LoadParm()
        lp.load(smbconf)

        # open a DB connection to the restored DB
        private_dir = os.path.join(targetdir, 'private')
        samdb_path = os.path.join(private_dir, 'sam.ldb')
        samdb = SamDB(url=samdb_path, session_info=system_session(), lp=lp)

        # Create account using the join_add_objects function in the join object
        # We need namingContexts, account control flags, and the sid saved by
        # the backup process.
        res = samdb.search(base="",
                           scope=ldb.SCOPE_BASE,
                           attrs=['namingContexts'])
        ncs = [str(r) for r in res[0].get('namingContexts')]

        creds = credopts.get_credentials(lp)
        ctx = DCJoinContext(logger,
                            creds=creds,
                            lp=lp,
                            forced_local_samdb=samdb,
                            netbios_name=newservername)
        ctx.nc_list = ncs
        ctx.full_nc_list = ncs
        ctx.userAccountControl = (samba.dsdb.UF_SERVER_TRUST_ACCOUNT
                                  | samba.dsdb.UF_TRUSTED_FOR_DELEGATION)

        # rewrite the smb.conf to make sure it uses the new targetdir settings.
        # (This doesn't update all filepaths in a customized config, but it
        # corrects the same paths that get set by a new provision)
        logger.info('Updating basic smb.conf settings...')
        make_smbconf(smbconf,
                     newservername,
                     ctx.domain_name,
                     ctx.realm,
                     targetdir,
                     lp=lp,
                     serverrole="active directory domain controller")

        # Get the SID saved by the backup process and create account
        res = samdb.search(base=ldb.Dn(samdb, "@SAMBA_DSDB"),
                           scope=ldb.SCOPE_BASE,
                           attrs=['sidForRestore', 'backupRename'])
        is_rename = True if 'backupRename' in res[0] else False
        sid = res[0].get('sidForRestore')[0]
        logger.info('Creating account with SID: ' + str(sid))
        ctx.join_add_objects(specified_sid=dom_sid(sid))

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

        # if we renamed the backed-up domain, then we need to add the DNS
        # objects for the new realm (we do this in the restore, now that we
        # know the new DC's IP address)
        if is_rename:
            self.register_dns_zone(logger, samdb, lp, ctx.ntds_guid, host_ip,
                                   host_ip6)

        secrets_path = os.path.join(private_dir, 'secrets.ldb')
        secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp)
        secretsdb_self_join(secrets_ldb,
                            domain=ctx.domain_name,
                            realm=ctx.realm,
                            dnsdomain=ctx.dnsdomain,
                            netbiosname=ctx.myname,
                            domainsid=ctx.domsid,
                            machinepass=ctx.acct_pass,
                            key_version_number=ctx.key_version_number,
                            secure_channel_type=misc.SEC_CHAN_BDC)

        # Seize DNS roles
        domain_dn = samdb.domain_dn()
        forest_dn = samba.dn_from_dns_name(samdb.forest_dns_name())
        domaindns_dn = ("CN=Infrastructure,DC=DomainDnsZones,", domain_dn)
        forestdns_dn = ("CN=Infrastructure,DC=ForestDnsZones,", forest_dn)
        for dn_prefix, dns_dn in [forestdns_dn, domaindns_dn]:
            if dns_dn not in ncs:
                continue
            full_dn = dn_prefix + dns_dn
            m = ldb.Message()
            m.dn = ldb.Dn(samdb, full_dn)
            m["fSMORoleOwner"] = ldb.MessageElement(samdb.get_dsServiceName(),
                                                    ldb.FLAG_MOD_REPLACE,
                                                    "fSMORoleOwner")
            samdb.modify(m)

        # Seize other roles
        for role in ['rid', 'pdc', 'naming', 'infrastructure', 'schema']:
            self.seize_role(role, samdb, force=True)

        # Get all DCs and remove them (this ensures these DCs cannot
        # replicate because they will not have a password)
        search_expr = "(&(objectClass=Server)(serverReference=*))"
        res = samdb.search(samdb.get_config_basedn(),
                           scope=ldb.SCOPE_SUBTREE,
                           expression=search_expr)
        for m in res:
            cn = m.get('cn')[0]
            if cn != newservername:
                remove_dc(samdb, logger, cn)

        # Remove the repsFrom and repsTo from each NC to ensure we do
        # not try (and fail) to talk to the old DCs
        for nc in ncs:
            msg = ldb.Message()
            msg.dn = ldb.Dn(samdb, nc)

            msg["repsFrom"] = ldb.MessageElement([], ldb.FLAG_MOD_REPLACE,
                                                 "repsFrom")
            msg["repsTo"] = ldb.MessageElement([], ldb.FLAG_MOD_REPLACE,
                                               "repsTo")
            samdb.modify(msg)

        # Update the krbtgt passwords twice, ensuring no tickets from
        # the old domain are valid
        update_krbtgt_account_password(samdb)
        update_krbtgt_account_password(samdb)

        # restore the sysvol directory from the backup tar file, including the
        # original NTACLs. Note that the backup_restore() will fail if not root
        sysvol_tar = os.path.join(targetdir, 'sysvol.tar.gz')
        dest_sysvol_dir = lp.get('path', 'sysvol')
        if not os.path.exists(dest_sysvol_dir):
            os.makedirs(dest_sysvol_dir)
        backup_restore(sysvol_tar, dest_sysvol_dir, samdb, smbconf)
        os.remove(sysvol_tar)

        # fix up any stale links to the old DCs we just removed
        logger.info("Fixing up any remaining references to the old DCs...")
        self.fix_old_dc_references(samdb)

        # Remove DB markers added by the backup process
        m = ldb.Message()
        m.dn = ldb.Dn(samdb, "@SAMBA_DSDB")
        m["backupDate"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
                                             "backupDate")
        m["sidForRestore"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
                                                "sidForRestore")
        if is_rename:
            m["backupRename"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
                                                   "backupRename")
        samdb.modify(m)

        logger.info("Backup file successfully restored to %s" % targetdir)
        logger.info("Please check the smb.conf settings are correct before "
                    "starting samba.")
Exemple #27
0
class PasswordTests(PasswordTestCase):

    def setUp(self):
        super(PasswordTests, self).setUp()
        self.ldb = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp)

        # Gets back the basedn
        base_dn = self.ldb.domain_dn()

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # permit password changes during this test
        self.allow_password_changes()

        self.base_dn = self.ldb.domain_dn()

        # (Re)adds the test user "testuser" with no password atm
        delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser,cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": "testuser"})

        # Tests a password change when we don't have any password yet with a
        # wrong old password
        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: noPassword
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            # Windows (2008 at least) seems to have some small bug here: it
            # returns "0000056A" on longer (always wrong) previous passwords.
            self.assertTrue('00000056' in msg)

        # Sets the initial user password with a "special" password change
        # I think that this internally is a password set operation and it can
        # only be performed by someone which has password set privileges on the
        # account (at least in s4 we do handle it like that).
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: thatsAcomplPASS1
""")

        # But in the other way around this special syntax doesn't work
        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
""")
            self.fail()
        except LdbError as e1:
            (num, _) = e1.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        # Enables the user account
        self.ldb.enable_account("(sAMAccountName=testuser)")

        # Open a second LDB connection with the user credentials. Use the
        # command line credentials for informations like the domain, the realm
        # and the workstation.
        creds2 = Credentials()
        creds2.set_username("testuser")
        creds2.set_password("thatsAcomplPASS1")
        creds2.set_domain(creds.get_domain())
        creds2.set_realm(creds.get_realm())
        creds2.set_workstation(creds.get_workstation())
        creds2.set_gensec_features(creds2.get_gensec_features()
                                                          | gensec.FEATURE_SEAL)
        self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)

    def test_unicodePwd_hash_set(self):
        """Performs a password hash set operation on 'unicodePwd' which should be prevented"""
        # Notice: Direct hash password sets should never work

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
          "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e2:
            (num, _) = e2.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_unicodePwd_hash_change(self):
        """Performs a password hash change operation on 'unicodePwd' which should be prevented"""
        # Notice: Direct hash password changes should never work

        # Hash password changes should never work
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd: XXXXXXXXXXXXXXXX
add: unicodePwd
unicodePwd: YYYYYYYYYYYYYYYY
""")
            self.fail()
        except LdbError as e3:
            (num, _) = e3.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

    def test_unicodePwd_clear_set(self):
        """Performs a password cleartext set operation on 'unicodePwd'"""

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
          FLAG_MOD_REPLACE, "unicodePwd")
        self.ldb.modify(m)

    def test_unicodePwd_clear_change(self):
        """Performs a password cleartext change operation on 'unicodePwd'"""

        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')).decode('utf8') + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
""")

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')).decode('utf8') + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')).decode('utf8') + """
""")
            self.fail()
        except LdbError as e4:
            (num, msg) = e4.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')).decode('utf8') + """
""")
            self.fail()
        except LdbError as e5:
            (num, msg) = e5.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('0000052D' in msg)

    def test_dBCSPwd_hash_set(self):
        """Performs a password hash set operation on 'dBCSPwd' which should be prevented"""
        # Notice: Direct hash password sets should never work

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
          "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e6:
            (num, _) = e6.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_dBCSPwd_hash_change(self):
        """Performs a password hash change operation on 'dBCSPwd' which should be prevented"""
        # Notice: Direct hash password changes should never work

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: dBCSPwd
dBCSPwd: XXXXXXXXXXXXXXXX
add: dBCSPwd
dBCSPwd: YYYYYYYYYYYYYYYY
""")
            self.fail()
        except LdbError as e7:
            (num, _) = e7.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_userPassword_clear_set(self):
        """Performs a password cleartext set operation on 'userPassword'"""
        # Notice: This works only against Windows if "dSHeuristics" has been set
        # properly

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

    def test_userPassword_clear_change(self):
        """Performs a password cleartext change operation on 'userPassword'"""
        # Notice: This works only against Windows if "dSHeuristics" has been set
        # properly

        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS3
add: userPassword
userPassword: thatsAcomplPASS4
""")
            self.fail()
        except LdbError as e8:
            (num, msg) = e8.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e9:
            (num, msg) = e9.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('0000052D' in msg)

    def test_clearTextPassword_clear_set(self):
        """Performs a password cleartext set operation on 'clearTextPassword'"""
        # Notice: This never works against Windows - only supported by us

        try:
            m = Message()
            m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
            m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
              FLAG_MOD_REPLACE, "clearTextPassword")
            self.ldb.modify(m)
            # this passes against s4
        except LdbError as e10:
            (num, msg) = e10.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                raise LdbError(num, msg)

    def test_clearTextPassword_clear_change(self):
        """Performs a password cleartext change operation on 'clearTextPassword'"""
        # Notice: This never works against Windows - only supported by us

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')).decode('utf8') + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')).decode('utf8') + """
""")
            # this passes against s4
        except LdbError as e11:
            (num, msg) = e11.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                raise LdbError(num, msg)

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')).decode('utf8') + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')).decode('utf8') + """
""")
            self.fail()
        except LdbError as e12:
            (num, msg) = e12.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
                self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')).decode('utf8') + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')).decode('utf8') + """
""")
            self.fail()
        except LdbError as e13:
            (num, msg) = e13.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
                self.assertTrue('0000052D' in msg)

    def test_failures(self):
        """Performs some failure testing"""

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e14:
            (num, _) = e14.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e15:
            (num, _) = e15.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
""")
            self.fail()
        except LdbError as e16:
            (num, _) = e16.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
""")
            self.fail()
        except LdbError as e17:
            (num, _) = e17.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e18:
            (num, _) = e18.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e19:
            (num, _) = e19.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e20:
            (num, _) = e20.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e21:
            (num, _) = e21.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e22:
            (num, _) = e22.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e23:
            (num, _) = e23.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e24:
            (num, _) = e24.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e25:
            (num, _) = e25.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e26:
            (num, _) = e26.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e27:
            (num, _) = e27.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
""")
            self.fail()
        except LdbError as e28:
            (num, _) = e28.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
""")
            self.fail()
        except LdbError as e29:
            (num, _) = e29.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        # Reverse order does work
        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS2
delete: userPassword
userPassword: thatsAcomplPASS1
""")

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS2
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')).decode('utf8') + """
""")
             # this passes against s4
        except LdbError as e30:
            (num, _) = e30.args
            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')).decode('utf8') + """
add: userPassword
userPassword: thatsAcomplPASS4
""")
             # this passes against s4
        except LdbError as e31:
            (num, _) = e31.args
            self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)

        # Several password changes at once are allowed
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
replace: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS2
""")

        # Several password changes at once are allowed
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
replace: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
replace: userPassword
userPassword: thatsAcomplPASS4
""")

        # This surprisingly should work
        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser2,cn=users," + self.base_dn,
             "objectclass": "user",
             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })

        # This surprisingly should work
        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser2,cn=users," + self.base_dn,
             "objectclass": "user",
             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })

    def test_empty_passwords(self):
        print("Performs some empty passwords testing")

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "unicodePwd": [] })
            self.fail()
        except LdbError as e32:
            (num, _) = e32.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "dBCSPwd": [] })
            self.fail()
        except LdbError as e33:
            (num, _) = e33.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "userPassword": [] })
            self.fail()
        except LdbError as e34:
            (num, _) = e34.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "clearTextPassword": [] })
            self.fail()
        except LdbError as e35:
            (num, _) = e35.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e36:
            (num, _) = e36.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e37:
            (num, _) = e37.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e38:
            (num, _) = e38.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e39:
            (num, _) = e39.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e40:
            (num, _) = e40.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e41:
            (num, _) = e41.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e42:
            (num, _) = e42.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e43:
            (num, _) = e43.args
            self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e44:
            (num, _) = e44.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e45:
            (num, _) = e45.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e46:
            (num, _) = e46.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e47:
            (num, _) = e47.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

    def test_plain_userPassword(self):
        print("Performs testing about the standard 'userPassword' behaviour")

        # Delete the "dSHeuristics"
        self.ldb.set_dsheuristics(None)

        time.sleep(1) # This switching time is strictly needed!

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword2")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_DELETE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
        self.ldb.set_dsheuristics("000000000")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword3")

        # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
        self.ldb.set_dsheuristics("000000002")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword4")

        # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
        self.ldb.set_dsheuristics("000000001")

    def test_modify_dsheuristics_userPassword(self):
        print("Performs testing about reading userPassword between dsHeuristic modifies")

        # Make sure userPassword cannot be read
        self.ldb.set_dsheuristics("000000000")

        # Open a new connection (with dsHeuristic=000000000)
        ldb1 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Set userPassword to be read
        # This setting only affects newer connections (ldb2)
        ldb1.set_dsheuristics("000000001")
        time.sleep(1)

        m = Message()
        m.dn = Dn(ldb1, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS1", FLAG_MOD_REPLACE,
          "userPassword")
        ldb1.modify(m)

        res = ldb1.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword cannot be read, it wasn't set, instead the
        # password was
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Open another new connection (with dsHeuristic=000000001)
        ldb2 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        res = ldb2.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # Check on the new connection that userPassword was not stored
        # from ldb1 or is not readable
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Set userPassword to be readable
        # This setting does not affect this connection
        ldb2.set_dsheuristics("000000000")
        time.sleep(1)

        res = ldb2.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # Check that userPassword was not stored from ldb1
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        m = Message()
        m.dn = Dn(ldb2, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
          "userPassword")
        ldb2.modify(m)

        res = ldb2.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # Check despite setting it with userPassword support disabled
        # on this connection it should still not be readable
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Only password from ldb1 is the user's password
        creds2 = Credentials()
        creds2.set_username("testuser")
        creds2.set_password("thatsAcomplPASS1")
        creds2.set_domain(creds.get_domain())
        creds2.set_realm(creds.get_realm())
        creds2.set_workstation(creds.get_workstation())
        creds2.set_gensec_features(creds2.get_gensec_features()
                                   | gensec.FEATURE_SEAL)

        try:
            SamDB(url=host, credentials=creds2, lp=lp)
        except:
            self.fail("testuser used the wrong password")

        ldb3 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Check that userPassword was stored from ldb2
        res = ldb3.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword can be read
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "thatsAcomplPASS2")

        # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
        self.ldb.set_dsheuristics("000000001")

        ldb4 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Check that userPassword that was stored from ldb2
        res = ldb4.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword can be not be read
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

    def test_zero_length(self):
        # Get the old "minPwdLength"
        minPwdLength = self.ldb.get_minPwdLength()
        # Set it temporarely to "0"
        self.ldb.set_minPwdLength("0")

        # Get the old "pwdProperties"
        pwdProperties = self.ldb.get_pwdProperties()
        # Set them temporarely to "0" (to deactivate eventually the complexity)
        self.ldb.set_pwdProperties("0")

        self.ldb.setpassword("(sAMAccountName=testuser)", "")

        # Reset the "pwdProperties" as they were before
        self.ldb.set_pwdProperties(pwdProperties)

        # Reset the "minPwdLength" as it was before
        self.ldb.set_minPwdLength(minPwdLength)

    def test_pw_change_delete_no_value_userPassword(self):
        """Test password change with userPassword where the delete attribute doesn't have a value"""

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: thatsAcomplPASS1
""")
        except LdbError, (num, msg):
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
        else:
Exemple #28
0
class SiteCoverageTests(samba.tests.TestCase):

    def setUp(self):
        self.prefix = "kcc_"
        self.lp = samba.tests.env_loadparm()

        self.sites = {}
        self.site_links = {}

        self.creds = Credentials()
        self.creds.guess(self.lp)
        self.session = system_session()

        self.samdb = SamDB(session_info=self.session,
                           credentials=self.creds,
                           lp=self.lp)

    def tearDown(self):
        self.samdb.transaction_start()

        for site in self.sites:
            delete_force(self.samdb, site, controls=['tree_delete:1'])

        for site_link in self.site_links:
            delete_force(self.samdb, site_link)

        self.samdb.transaction_commit()

    def _add_server(self, name, site):
        dn = "CN={},CN=Servers,{}".format(name, site)
        self.samdb.add({
            "dn": dn,
            "objectClass": "server",
            "serverReference": self.samdb.domain_dn()
        })
        return dn

    def _add_site(self, name):
        dn = "CN={},CN=Sites,{}".format(
            name, self.samdb.get_config_basedn()
        )
        self.samdb.add({
            "dn": dn,
            "objectClass": "site"
        })
        self.samdb.add({
            "dn": "CN=Servers," + dn,
            "objectClass": ["serversContainer"]
        })

        self.sites[dn] = name
        return dn, name.lower()

    def _add_site_link(self, name, links=[], cost=100):
        dn = "CN={},CN=IP,CN=Inter-Site Transports,CN=Sites,{}".format(
            name, self.samdb.get_config_basedn()
        )
        self.samdb.add({
            "dn": dn,
            "objectClass": "siteLink",
            "cost": str(cost),
            "siteList": links
        })
        self.site_links[dn] = name
        return dn

    def test_single_site_link_same_dc_count(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)

        self._add_site_link(self.prefix + "link",
                            [site1, site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_single_site_link_different_dc_count(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "ABCD" + '2', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)
        self._add_server(self.prefix + "BCDE" + '3', site2)

        self._add_site_link(self.prefix + "link",
                            [site1, site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_two_site_links_same_cost(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "ABCD" + '2', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)
        self._add_server(self.prefix + "BCDE" + '3', site2)

        self._add_site_link(self.prefix + "link1",
                            [site1, uncovered_dn])
        self._add_site_link(self.prefix + "link2",
                            [site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_two_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)

        self._add_site_link(self.prefix + "link1",
                            [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2",
                            [site2, uncovered_dn],
                            cost=75)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_three_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)

        self._add_site_link(self.prefix + "link1",
                            [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2",
                            [site2, uncovered_dn],
                            cost=75)
        self._add_site_link(self.prefix + "link3",
                            [site3, uncovered_dn],
                            cost=60)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_three_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)

        self._add_site_link(self.prefix + "link1",
                            [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2",
                            [site2, uncovered_dn],
                            cost=75)
        self._add_site_link(self.prefix + "link3",
                            [site3, uncovered_dn],
                            cost=50)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_complex_setup_with_multiple_uncovered_sites(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        site4, name4 = self._add_site(self.prefix + "1234")
        site5, name5 = self._add_site(self.prefix + "2345")
        site6, name6 = self._add_site(self.prefix + "3456")

        uncovered_dn1, uncovered1 = self._add_site(self.prefix + "uncovered1")
        uncovered_dn2, uncovered2 = self._add_site(self.prefix + "uncovered2")
        uncovered_dn3, uncovered3 = self._add_site(self.prefix + "uncovered3")

        # Site Link Cluster 1 - Server List
        self._add_server(self.prefix + "ABCD" + '1', site1)

        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)

        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)
        self._add_server(self.prefix + "CDEF" + '3', site3)

        # Site Link Cluster 2 - Server List
        self._add_server(self.prefix + "1234" + '1', site4)
        self._add_server(self.prefix + "1234" + '2', site4)

        self._add_server(self.prefix + "2345" + '1', site5)
        self._add_server(self.prefix + "2345" + '2', site5)

        self._add_server(self.prefix + "3456" + '1', site6)

        # Join to Uncovered1 (preference to site link cluster 1)
        self._add_site_link(self.prefix + "link1A",
                            [site1, site2, site3, uncovered_dn1],
                            cost=49)
        self._add_site_link(self.prefix + "link2A",
                            [site4, site5, site6, uncovered_dn1],
                            cost=50)

        # Join to Uncovered2 (no preferene on site links)
        self._add_site_link(self.prefix + "link1B",
                            [site1, site2, site3, uncovered_dn2],
                            cost=50)
        self._add_site_link(self.prefix + "link2B",
                            [site4, site5, site6, uncovered_dn2],
                            cost=50)

        # Join to Uncovered3 (preference to site link cluster 2)
        self._add_site_link(self.prefix + "link1C",
                            [site1, site2, site3, uncovered_dn3],
                            cost=50)
        self._add_site_link(self.prefix + "link2C",
                            [site4, site5, site6, uncovered_dn3],
                            cost=49)

        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([uncovered1, uncovered2], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name4)
        to_cover.sort()

        self.assertEqual([uncovered2, uncovered3], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name5)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name6)
        to_cover.sort()

        self.assertEqual([], to_cover)

        for to_check in [uncovered1, uncovered2, uncovered3]:
            to_cover = uncovered_sites_to_cover(self.samdb, to_check)
            to_cover.sort()

            self.assertEqual([], to_cover)
Exemple #29
0
class PassWordHashTests(TestCase):
    def setUp(self):
        self.lp = samba.tests.env_loadparm()
        super(PassWordHashTests, self).setUp()

    def set_store_cleartext(self, cleartext):
        # get the current pwdProperties
        pwdProperties = self.ldb.get_pwdProperties()
        # update the clear-text properties flag
        props = int(pwdProperties)
        if cleartext:
            props |= DOMAIN_PASSWORD_STORE_CLEARTEXT
        else:
            props &= ~DOMAIN_PASSWORD_STORE_CLEARTEXT
        self.ldb.set_pwdProperties(str(props))

    # Add a user to ldb, this will exercise the password_hash code
    # and calculate the appropriate supplemental credentials
    def add_user(self, options=None, clear_text=False, ldb=None):
        # set any needed options
        if options is not None:
            for (option, value) in options:
                self.lp.set(option, value)

        if ldb is None:
            self.creds = Credentials()
            self.session = system_session()
            self.creds.guess(self.lp)
            self.session = system_session()
            self.ldb = SamDB(session_info=self.session,
                             credentials=self.creds,
                             lp=self.lp)
        else:
            self.ldb = ldb

        res = self.ldb.search(base=self.ldb.get_config_basedn(),
                              expression="ncName=%s" %
                              self.ldb.get_default_basedn(),
                              attrs=["nETBIOSName"])
        self.netbios_domain = str(res[0]["nETBIOSName"][0])
        self.dns_domain = self.ldb.domain_dns_name()

        # Gets back the basedn
        base_dn = self.ldb.domain_dn()

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # permit password changes during this test
        PasswordCommon.allow_password_changes(self, self.ldb)

        self.base_dn = self.ldb.domain_dn()

        account_control = 0
        if clear_text:
            # Restore the current domain setting on exit.
            pwdProperties = self.ldb.get_pwdProperties()
            self.addCleanup(self.ldb.set_pwdProperties, pwdProperties)
            # Update the domain setting
            self.set_store_cleartext(clear_text)
            account_control |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED

        # (Re)adds the test user USER_NAME with password USER_PASS
        # and userPrincipalName UPN
        delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
        self.ldb.add({
            "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
            "objectclass": "user",
            "sAMAccountName": USER_NAME,
            "userPassword": USER_PASS,
            "userPrincipalName": UPN,
            "userAccountControl": str(account_control)
        })

    # Get the supplemental credentials for the user under test
    def get_supplemental_creds(self):
        base = "cn=" + USER_NAME + ",cn=users," + self.base_dn
        res = self.ldb.search(scope=ldb.SCOPE_BASE,
                              base=base,
                              attrs=["supplementalCredentials"])
        self.assertIs(True, len(res) > 0)
        obj = res[0]
        sc_blob = obj["supplementalCredentials"][0]
        sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob)
        return sc

    # Calculate and validate a Wdigest value
    def check_digest(self, user, realm, password, digest):
        expected = calc_digest(user, realm, password)
        actual = binascii.hexlify(bytearray(digest)).decode('utf8')
        error = "Digest expected[%s], actual[%s], " \
                "user[%s], realm[%s], pass[%s]" % \
                (expected, actual, user, realm, password)
        self.assertEquals(expected, actual, error)

    # Check all of the 29 expected WDigest values
    #
    def check_wdigests(self, digests):

        self.assertEquals(29, digests.num_hashes)

        # Using the n-1 pattern in the array indexes to make it easier
        # to check the tests against the spec and the samba-tool user tests.
        self.check_digest(USER_NAME, self.netbios_domain, USER_PASS,
                          digests.hashes[1 - 1].hash)
        self.check_digest(USER_NAME.lower(), self.netbios_domain.lower(),
                          USER_PASS, digests.hashes[2 - 1].hash)
        self.check_digest(USER_NAME.upper(), self.netbios_domain.upper(),
                          USER_PASS, digests.hashes[3 - 1].hash)
        self.check_digest(USER_NAME, self.netbios_domain.upper(), USER_PASS,
                          digests.hashes[4 - 1].hash)
        self.check_digest(USER_NAME, self.netbios_domain.lower(), USER_PASS,
                          digests.hashes[5 - 1].hash)
        self.check_digest(USER_NAME.upper(), self.netbios_domain.lower(),
                          USER_PASS, digests.hashes[6 - 1].hash)
        self.check_digest(USER_NAME.lower(), self.netbios_domain.upper(),
                          USER_PASS, digests.hashes[7 - 1].hash)
        self.check_digest(USER_NAME, self.dns_domain, USER_PASS,
                          digests.hashes[8 - 1].hash)
        self.check_digest(USER_NAME.lower(), self.dns_domain.lower(),
                          USER_PASS, digests.hashes[9 - 1].hash)
        self.check_digest(USER_NAME.upper(), self.dns_domain.upper(),
                          USER_PASS, digests.hashes[10 - 1].hash)
        self.check_digest(USER_NAME, self.dns_domain.upper(), USER_PASS,
                          digests.hashes[11 - 1].hash)
        self.check_digest(USER_NAME, self.dns_domain.lower(), USER_PASS,
                          digests.hashes[12 - 1].hash)
        self.check_digest(USER_NAME.upper(), self.dns_domain.lower(),
                          USER_PASS, digests.hashes[13 - 1].hash)
        self.check_digest(USER_NAME.lower(), self.dns_domain.upper(),
                          USER_PASS, digests.hashes[14 - 1].hash)
        self.check_digest(UPN, "", USER_PASS, digests.hashes[15 - 1].hash)
        self.check_digest(UPN.lower(), "", USER_PASS,
                          digests.hashes[16 - 1].hash)
        self.check_digest(UPN.upper(), "", USER_PASS,
                          digests.hashes[17 - 1].hash)

        name = "%s\\%s" % (self.netbios_domain, USER_NAME)
        self.check_digest(name, "", USER_PASS, digests.hashes[18 - 1].hash)

        name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
        self.check_digest(name, "", USER_PASS, digests.hashes[19 - 1].hash)

        name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
        self.check_digest(name, "", USER_PASS, digests.hashes[20 - 1].hash)
        self.check_digest(USER_NAME, "Digest", USER_PASS,
                          digests.hashes[21 - 1].hash)
        self.check_digest(USER_NAME.lower(), "Digest", USER_PASS,
                          digests.hashes[22 - 1].hash)
        self.check_digest(USER_NAME.upper(), "Digest", USER_PASS,
                          digests.hashes[23 - 1].hash)
        self.check_digest(UPN, "Digest", USER_PASS,
                          digests.hashes[24 - 1].hash)
        self.check_digest(UPN.lower(), "Digest", USER_PASS,
                          digests.hashes[25 - 1].hash)
        self.check_digest(UPN.upper(), "Digest", USER_PASS,
                          digests.hashes[26 - 1].hash)
        name = "%s\\%s" % (self.netbios_domain, USER_NAME)
        self.check_digest(name, "Digest", USER_PASS,
                          digests.hashes[27 - 1].hash)

        name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
        self.check_digest(name, "Digest", USER_PASS,
                          digests.hashes[28 - 1].hash)

        name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
        self.check_digest(name, "Digest", USER_PASS,
                          digests.hashes[29 - 1].hash)

    def checkUserPassword(self, up, expected):

        # Check we've received the correct number of hashes
        self.assertEquals(len(expected), up.num_hashes)

        i = 0
        for (tag, alg, rounds) in expected:
            self.assertEquals(tag, up.hashes[i].scheme)

            data = up.hashes[i].value.decode('utf8').split("$")
            # Check we got the expected crypt algorithm
            self.assertEquals(alg, data[1])

            if rounds is None:
                cmd = "$%s$%s" % (alg, data[2])
            else:
                cmd = "$%s$rounds=%d$%s" % (alg, rounds, data[3])

            # Calculate the expected hash value
            expected = crypt.crypt(USER_PASS, cmd)
            self.assertEquals(expected, up.hashes[i].value.decode('utf8'))
            i += 1

    # Check that the correct nt_hash was stored for userPassword
    def checkNtHash(self, password, nt_hash):
        creds = Credentials()
        creds.set_anonymous()
        creds.set_password(password)
        expected = creds.get_nt_hash()
        actual = bytearray(nt_hash)
        self.assertEquals(expected, actual)
def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file):
    """Routine to extract all objects and attributes that are relevent
    to the KCC algorithms from a DC database.

    The point of this function is to allow a programmer/debugger to
    extract an LDIF file with non-security relevent information from
    a DC database.  The LDIF file can then be used to "import" via
    the import_ldif() function this file into 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: LDAP database URL to extract info from
    :param ldif_file: output LDIF file name to create
    """
    try:
        samdb = SamDB(url=dburl,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)
    except ldb.LdbError as e:
        (enum, estr) = e.args
        raise LdifError("Unable to open sam database (%s) : %s" %
                        (dburl, estr))

    if os.path.exists(ldif_file):
        raise LdifError("Specify a file (%s) that doesn't already exist." %
                        ldif_file)

    try:
        f = open(ldif_file, "w")
    except IOError as ioerr:
        raise LdifError("Unable to open (%s) : %s" % (ldif_file, str(ioerr)))

    try:
        # Query Partitions
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "objectSid",
            "Enabled", "systemFlags", "dnsRoot", "nCName",
            "msDS-NC-Replica-Locations", "msDS-NC-RO-Replica-Locations"
        ]

        sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
        res = samdb.search(base=sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRef)")

        # Write partitions output
        write_search_result(samdb, f, res)

        # Query cross reference container
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "fSMORoleOwner",
            "systemFlags", "msDS-Behavior-Version", "msDS-EnabledFeature"
        ]

        sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
        res = samdb.search(base=sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRefContainer)")

        # Write cross reference container output
        write_search_result(samdb, f, res)

        # Query Sites
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "systemFlags"
        ]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        sites = samdb.search(base=sstr,
                             scope=ldb.SCOPE_SUBTREE,
                             attrs=attrs,
                             expression="(objectClass=site)")

        # Write sites output
        write_search_result(samdb, f, sites)

        # Query NTDS Site Settings
        for msg in sites:
            sitestr = str(msg.dn)

            attrs = [
                "objectClass", "objectGUID", "cn", "whenChanged",
                "interSiteTopologyGenerator", "interSiteTopologyFailover",
                "schedule", "options"
            ]

            sstr = "CN=NTDS Site Settings,%s" % sitestr
            res = samdb.search(base=sstr, scope=ldb.SCOPE_BASE, attrs=attrs)

            # Write Site Settings output
            write_search_result(samdb, f, res)

        # Naming context list
        nclist = []

        # Query Directory Service Agents
        for msg in sites:
            sstr = str(msg.dn)

            ncattrs = [
                "hasMasterNCs", "msDS-hasMasterNCs", "hasPartialReplicaNCs",
                "msDS-HasDomainNCs", "msDS-hasFullReplicaNCs",
                "msDS-HasInstantiatedNCs"
            ]
            attrs = [
                "objectClass", "objectGUID", "cn", "whenChanged",
                "invocationID", "options", "msDS-isRODC",
                "msDS-Behavior-Version"
            ]

            res = samdb.search(base=sstr,
                               scope=ldb.SCOPE_SUBTREE,
                               attrs=attrs + ncattrs,
                               expression="(objectClass=nTDSDSA)")

            # Spin thru all the DSAs looking for NC replicas
            # and build a list of all possible Naming Contexts
            # for subsequent retrieval below
            for msg in res:
                for k in msg.keys():
                    if k in ncattrs:
                        for value in msg[k]:
                            # Some of these have binary DNs so
                            # use dsdb_Dn to split out relevent parts
                            dsdn = dsdb_Dn(samdb, value)
                            dnstr = str(dsdn.dn)
                            if dnstr not in nclist:
                                nclist.append(dnstr)

            # Write DSA output
            write_search_result(samdb, f, res)

        # Query NTDS Connections
        for msg in sites:
            sstr = str(msg.dn)

            attrs = [
                "objectClass", "objectGUID", "cn", "whenChanged", "options",
                "whenCreated", "enabledConnection", "schedule",
                "transportType", "fromServer", "systemFlags"
            ]

            res = samdb.search(base=sstr,
                               scope=ldb.SCOPE_SUBTREE,
                               attrs=attrs,
                               expression="(objectClass=nTDSConnection)")
            # Write NTDS Connection output
            write_search_result(samdb, f, res)

        # Query Intersite transports
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "options",
            "name", "bridgeheadServerListBL", "transportAddressAttribute"
        ]

        sstr = "CN=Inter-Site Transports,CN=Sites,%s" % \
               samdb.get_config_basedn()
        res = samdb.search(sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=interSiteTransport)")

        # Write inter-site transport output
        write_search_result(samdb, f, res)

        # Query siteLink
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "systemFlags",
            "options", "schedule", "replInterval", "siteList", "cost"
        ]

        sstr = "CN=Sites,%s" % \
               samdb.get_config_basedn()
        res = samdb.search(sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=siteLink)",
                           controls=['extended_dn:0'])

        # Write siteLink output
        write_search_result(samdb, f, res)

        # Query siteLinkBridge
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "siteLinkList"
        ]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=siteLinkBridge)")

        # Write siteLinkBridge output
        write_search_result(samdb, f, res)

        # Query servers containers
        # Needed for samdb.server_site_name()
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "systemFlags"
        ]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=serversContainer)")

        # Write servers container output
        write_search_result(samdb, f, res)

        # Query servers
        # Needed because some transport interfaces refer back to
        # attributes found in the server object.   Also needed
        # so extended-dn will be happy with dsServiceName in rootDSE
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "systemFlags",
            "dNSHostName", "mailAddress"
        ]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr,
                           scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=server)")

        # Write server output
        write_search_result(samdb, f, res)

        # Query Naming Context replicas
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged", "objectSid",
            "fSMORoleOwner", "msDS-Behavior-Version", "repsFrom", "repsTo"
        ]

        for sstr in nclist:
            res = samdb.search(sstr, scope=ldb.SCOPE_BASE, attrs=attrs)

            # Write naming context output
            write_search_result(samdb, f, res)

        # Query rootDSE replicas
        attrs = [
            "objectClass", "objectGUID", "cn", "whenChanged",
            "rootDomainNamingContext", "configurationNamingContext",
            "schemaNamingContext", "defaultNamingContext", "dsServiceName"
        ]

        sstr = ""
        res = samdb.search(sstr, scope=ldb.SCOPE_BASE, attrs=attrs)

        # Record the rootDSE object as a dn as it
        # would appear in the base ldb file.  We have
        # to save it this way because we are going to
        # be importing as an abbreviated database.
        res[0].dn = ldb.Dn(samdb, "@ROOTDSE")

        # Write rootdse output
        write_search_result(samdb, f, res)

    except ldb.LdbError as e1:
        (enum, estr) = e1.args
        raise LdifError("Error processing (%s) : %s" % (sstr, estr))

    f.close()
class BasicDeleteTests(samba.tests.TestCase):

    def GUID_string(self, guid):
        return self.ldb.schema_format_value("objectGUID", guid)

    def setUp(self):
        super(BasicDeleteTests, self).setUp()
        self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp)

        self.base_dn = self.ldb.domain_dn()
        self.configuration_dn = self.ldb.get_config_basedn().get_linearized()

    def search_guid(self, guid):
        print "SEARCH by GUID %s" % self.GUID_string(guid)

        res = self.ldb.search(base="<GUID=%s>" % self.GUID_string(guid),
                         scope=SCOPE_BASE, controls=["show_deleted:1"])
        self.assertEquals(len(res), 1)
        return res[0]

    def search_dn(self,dn):
        print "SEARCH by DN %s" % dn

        res = self.ldb.search(expression="(objectClass=*)",
                         base=dn,
                         scope=SCOPE_BASE,
                         controls=["show_deleted:1"])
        self.assertEquals(len(res), 1)
        return res[0]

    def del_attr_values(self, delObj):
        print "Checking attributes for %s" % delObj["dn"]

        self.assertEquals(delObj["isDeleted"][0],"TRUE")
        self.assertTrue(not("objectCategory" in delObj))
        self.assertTrue(not("sAMAccountType" in delObj))

    def preserved_attributes_list(self, liveObj, delObj):
        print "Checking for preserved attributes list"

        preserved_list = ["nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
        "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
        "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
        "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
        "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
        "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
        "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated"]

        for a in liveObj:
            if a in preserved_list:
                self.assertTrue(a in delObj)

    def check_rdn(self, liveObj, delObj, rdnName):
        print "Checking for correct rDN"
        rdn=liveObj[rdnName][0]
        rdn2=delObj[rdnName][0]
        name2=delObj[rdnName][0]
        guid=liveObj["objectGUID"][0]
        self.assertEquals(rdn2, rdn + "\nDEL:" + self.GUID_string(guid))
        self.assertEquals(name2, rdn + "\nDEL:" + self.GUID_string(guid))

    def delete_deleted(self, ldb, dn):
        print "Testing the deletion of the already deleted dn %s" % dn

        try:
            ldb.delete(dn)
            self.fail()
        except LdbError, (num, _):
            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
Exemple #32
0
class PasswordTests(samba.tests.TestCase):

    def setUp(self):
        super(PasswordTests, self).setUp()
        self.ldb = SamDB(url=host, session_info=system_session(lp), credentials=creds, lp=lp)

        # Gets back the basedn
        base_dn = self.ldb.domain_dn()

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # Get the old "dSHeuristics" if it was set
        dsheuristics = self.ldb.get_dsheuristics()

        # Set the "dSHeuristics" to activate the correct "userPassword" behaviour
        self.ldb.set_dsheuristics("000000001")

        # Reset the "dSHeuristics" as they were before
        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)

        # Get the old "minPwdAge"
        minPwdAge = self.ldb.get_minPwdAge()

        # Set it temporarily to "0"
        self.ldb.set_minPwdAge("0")
        self.base_dn = self.ldb.domain_dn()

        # Reset the "minPwdAge" as it was before
        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)

        # (Re)adds the test user "testuser" with no password atm
        delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser,cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": "testuser"})

        # Tests a password change when we don't have any password yet with a
        # wrong old password
        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: noPassword
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e:
            (num, msg) = e.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            # Windows (2008 at least) seems to have some small bug here: it
            # returns "0000056A" on longer (always wrong) previous passwords.
            self.assertTrue('00000056' in msg)

        # Sets the initial user password with a "special" password change
        # I think that this internally is a password set operation and it can
        # only be performed by someone which has password set privileges on the
        # account (at least in s4 we do handle it like that).
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: thatsAcomplPASS1
""")

        # But in the other way around this special syntax doesn't work
        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
""")
            self.fail()
        except LdbError as e1:
            (num, _) = e1.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        # Enables the user account
        self.ldb.enable_account("(sAMAccountName=testuser)")

        # Open a second LDB connection with the user credentials. Use the
        # command line credentials for informations like the domain, the realm
        # and the workstation.
        creds2 = Credentials()
        creds2.set_username("testuser")
        creds2.set_password("thatsAcomplPASS1")
        creds2.set_domain(creds.get_domain())
        creds2.set_realm(creds.get_realm())
        creds2.set_workstation(creds.get_workstation())
        creds2.set_gensec_features(creds2.get_gensec_features()
                                                          | gensec.FEATURE_SEAL)
        self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)

    def test_unicodePwd_hash_set(self):
        """Performs a password hash set operation on 'unicodePwd' which should be prevented"""
        # Notice: Direct hash password sets should never work

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
          "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e2:
            (num, _) = e2.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_unicodePwd_hash_change(self):
        """Performs a password hash change operation on 'unicodePwd' which should be prevented"""
        # Notice: Direct hash password changes should never work

        # Hash password changes should never work
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd: XXXXXXXXXXXXXXXX
add: unicodePwd
unicodePwd: YYYYYYYYYYYYYYYY
""")
            self.fail()
        except LdbError as e3:
            (num, _) = e3.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

    def test_unicodePwd_clear_set(self):
        """Performs a password cleartext set operation on 'unicodePwd'"""

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
          FLAG_MOD_REPLACE, "unicodePwd")
        self.ldb.modify(m)

    def test_unicodePwd_clear_change(self):
        """Performs a password cleartext change operation on 'unicodePwd'"""

        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
""")

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
""")
            self.fail()
        except LdbError as e4:
            (num, msg) = e4.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
""")
            self.fail()
        except LdbError as e5:
            (num, msg) = e5.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('0000052D' in msg)

    def test_dBCSPwd_hash_set(self):
        """Performs a password hash set operation on 'dBCSPwd' which should be prevented"""
        # Notice: Direct hash password sets should never work

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
          "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e6:
            (num, _) = e6.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_dBCSPwd_hash_change(self):
        """Performs a password hash change operation on 'dBCSPwd' which should be prevented"""
        # Notice: Direct hash password changes should never work

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: dBCSPwd
dBCSPwd: XXXXXXXXXXXXXXXX
add: dBCSPwd
dBCSPwd: YYYYYYYYYYYYYYYY
""")
            self.fail()
        except LdbError as e7:
            (num, _) = e7.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

    def test_userPassword_clear_set(self):
        """Performs a password cleartext set operation on 'userPassword'"""
        # Notice: This works only against Windows if "dSHeuristics" has been set
        # properly

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

    def test_userPassword_clear_change(self):
        """Performs a password cleartext change operation on 'userPassword'"""
        # Notice: This works only against Windows if "dSHeuristics" has been set
        # properly

        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS3
add: userPassword
userPassword: thatsAcomplPASS4
""")
            self.fail()
        except LdbError as e8:
            (num, msg) = e8.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e9:
            (num, msg) = e9.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
            self.assertTrue('0000052D' in msg)

    def test_clearTextPassword_clear_set(self):
        """Performs a password cleartext set operation on 'clearTextPassword'"""
        # Notice: This never works against Windows - only supported by us

        try:
            m = Message()
            m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
            m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
              FLAG_MOD_REPLACE, "clearTextPassword")
            self.ldb.modify(m)
            # this passes against s4
        except LdbError as e10:
            (num, msg) = e10.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                raise LdbError(num, msg)

    def test_clearTextPassword_clear_change(self):
        """Performs a password cleartext change operation on 'clearTextPassword'"""
        # Notice: This never works against Windows - only supported by us

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
""")
            # this passes against s4
        except LdbError as e11:
            (num, msg) = e11.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                raise LdbError(num, msg)

        # Wrong old password
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS3".encode('utf-16-le')) + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
""")
            self.fail()
        except LdbError as e12:
            (num, msg) = e12.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
                self.assertTrue('00000056' in msg)

        # A change to the same password again will not work (password history)
        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
""")
            self.fail()
        except LdbError as e13:
            (num, msg) = e13.args
            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
            if num != ERR_NO_SUCH_ATTRIBUTE:
                self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
                self.assertTrue('0000052D' in msg)

    def test_failures(self):
        """Performs some failure testing"""

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e14:
            (num, _) = e14.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e15:
            (num, _) = e15.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
""")
            self.fail()
        except LdbError as e16:
            (num, _) = e16.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
""")
            self.fail()
        except LdbError as e17:
            (num, _) = e17.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e18:
            (num, _) = e18.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS1
""")
            self.fail()
        except LdbError as e19:
            (num, _) = e19.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e20:
            (num, _) = e20.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e21:
            (num, _) = e21.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e22:
            (num, _) = e22.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e23:
            (num, _) = e23.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e24:
            (num, _) = e24.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e25:
            (num, _) = e25.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e26:
            (num, _) = e26.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
""")
            self.fail()
        except LdbError as e27:
            (num, _) = e27.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        try:
            self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
""")
            self.fail()
        except LdbError as e28:
            (num, _) = e28.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS1
add: userPassword
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
""")
            self.fail()
        except LdbError as e29:
            (num, _) = e29.args
            self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS)

        # Reverse order does work
        self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
add: userPassword
userPassword: thatsAcomplPASS2
delete: userPassword
userPassword: thatsAcomplPASS1
""")

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
userPassword: thatsAcomplPASS2
add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
""")
             # this passes against s4
        except LdbError as e30:
            (num, _) = e30.args
            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
add: userPassword
userPassword: thatsAcomplPASS4
""")
             # this passes against s4
        except LdbError as e31:
            (num, _) = e31.args
            self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)

        # Several password changes at once are allowed
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
replace: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS2
""")

        # Several password changes at once are allowed
        self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
replace: userPassword
userPassword: thatsAcomplPASS1
userPassword: thatsAcomplPASS2
replace: userPassword
userPassword: thatsAcomplPASS3
replace: userPassword
userPassword: thatsAcomplPASS4
""")

        # This surprisingly should work
        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser2,cn=users," + self.base_dn,
             "objectclass": "user",
             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })

        # This surprisingly should work
        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=testuser2,cn=users," + self.base_dn,
             "objectclass": "user",
             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })

    def test_empty_passwords(self):
        print("Performs some empty passwords testing")

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "unicodePwd": [] })
            self.fail()
        except LdbError as e32:
            (num, _) = e32.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "dBCSPwd": [] })
            self.fail()
        except LdbError as e33:
            (num, _) = e33.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "userPassword": [] })
            self.fail()
        except LdbError as e34:
            (num, _) = e34.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        try:
            self.ldb.add({
                 "dn": "cn=testuser2,cn=users," + self.base_dn,
                 "objectclass": "user",
                 "clearTextPassword": [] })
            self.fail()
        except LdbError as e35:
            (num, _) = e35.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_ADD, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e36:
            (num, _) = e36.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_ADD, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e37:
            (num, _) = e37.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_ADD, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e38:
            (num, _) = e38.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_ADD, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e39:
            (num, _) = e39.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_REPLACE, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e40:
            (num, _) = e40.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_REPLACE, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e41:
            (num, _) = e41.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_REPLACE, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e42:
            (num, _) = e42.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_REPLACE, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e43:
            (num, _) = e43.args
            self.assertTrue(num == ERR_UNWILLING_TO_PERFORM or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["unicodePwd"] = MessageElement([], FLAG_MOD_DELETE, "unicodePwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e44:
            (num, _) = e44.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["dBCSPwd"] = MessageElement([], FLAG_MOD_DELETE, "dBCSPwd")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e45:
            (num, _) = e45.args
            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_DELETE, "userPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e46:
            (num, _) = e46.args
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["clearTextPassword"] = MessageElement([], FLAG_MOD_DELETE, "clearTextPassword")
        try:
            self.ldb.modify(m)
            self.fail()
        except LdbError as e47:
            (num, _) = e47.args
            self.assertTrue(num == ERR_CONSTRAINT_VIOLATION or
                            num == ERR_NO_SUCH_ATTRIBUTE) # for Windows

    def test_plain_userPassword(self):
        print("Performs testing about the standard 'userPassword' behaviour")

        # Delete the "dSHeuristics"
        self.ldb.set_dsheuristics(None)

        time.sleep(1) # This switching time is strictly needed!

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword", FLAG_MOD_ADD,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword2", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword2")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement([], FLAG_MOD_DELETE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
        self.ldb.set_dsheuristics("000000000")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword3", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword3")

        # Set the test "dSHeuristics" to deactivate "userPassword" pwd changes
        self.ldb.set_dsheuristics("000000002")

        m = Message()
        m.dn = Dn(self.ldb, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("myPassword4", FLAG_MOD_REPLACE,
          "userPassword")
        self.ldb.modify(m)

        res = self.ldb.search("cn=testuser,cn=users," + self.base_dn,
                         scope=SCOPE_BASE, attrs=["userPassword"])
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "myPassword4")

        # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
        self.ldb.set_dsheuristics("000000001")

    def test_modify_dsheuristics_userPassword(self):
        print("Performs testing about reading userPassword between dsHeuristic modifies")

        # Make sure userPassword cannot be read
        self.ldb.set_dsheuristics("000000000")

        # Open a new connection (with dsHeuristic=000000000)
        ldb1 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Set userPassword to be read
        # This setting only affects newer connections (ldb2)
        ldb1.set_dsheuristics("000000001")
        time.sleep(1)

        m = Message()
        m.dn = Dn(ldb1, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS1", FLAG_MOD_REPLACE,
          "userPassword")
        ldb1.modify(m)

        res = ldb1.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword cannot be read, despite the dsHeuristic setting
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        # Open another new connection (with dsHeuristic=000000001)
        ldb2 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Set userPassword to be unreadable
        # This setting does not affect this connection
        ldb2.set_dsheuristics("000000000")
        time.sleep(1)

        res = ldb2.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # Check that userPassword was not stored from ldb1
        self.assertTrue(len(res) == 1)
        self.assertFalse("userPassword" in res[0])

        m = Message()
        m.dn = Dn(ldb2, "cn=testuser,cn=users," + self.base_dn)
        m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
          "userPassword")
        ldb2.modify(m)

        res = ldb2.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword can be read in this connection
        # This is regardless of the current dsHeuristics setting
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "thatsAcomplPASS2")

        # Only password from ldb1 is the user's password
        creds2 = Credentials()
        creds2.set_username("testuser")
        creds2.set_password("thatsAcomplPASS1")
        creds2.set_domain(creds.get_domain())
        creds2.set_realm(creds.get_realm())
        creds2.set_workstation(creds.get_workstation())
        creds2.set_gensec_features(creds2.get_gensec_features()
                                   | gensec.FEATURE_SEAL)

        try:
            SamDB(url=host, credentials=creds2, lp=lp)
        except:
            self.fail("testuser used the wrong password")

        ldb3 = SamDB(url=host, session_info=system_session(lp),
                     credentials=creds, lp=lp)

        # Check that userPassword was stored from ldb2
        res = ldb3.search("cn=testuser,cn=users," + self.base_dn,
                          scope=SCOPE_BASE, attrs=["userPassword"])

        # userPassword can be read
        self.assertTrue(len(res) == 1)
        self.assertTrue("userPassword" in res[0])
        self.assertEquals(res[0]["userPassword"][0], "thatsAcomplPASS2")

        # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
        self.ldb.set_dsheuristics("000000001")

    def test_zero_length(self):
        # Get the old "minPwdLength"
        minPwdLength = self.ldb.get_minPwdLength()
        # Set it temporarely to "0"
        self.ldb.set_minPwdLength("0")

        # Get the old "pwdProperties"
        pwdProperties = self.ldb.get_pwdProperties()
        # Set them temporarely to "0" (to deactivate eventually the complexity)
        self.ldb.set_pwdProperties("0")

        self.ldb.setpassword("(sAMAccountName=testuser)", "")

        # Reset the "pwdProperties" as they were before
        self.ldb.set_pwdProperties(pwdProperties)

        # Reset the "minPwdLength" as it was before
        self.ldb.set_minPwdLength(minPwdLength)

    def test_pw_change_delete_no_value_userPassword(self):
        """Test password change with userPassword where the delete attribute doesn't have a value"""

        try:
            self.ldb2.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
add: userPassword
userPassword: thatsAcomplPASS1
""")
        except LdbError, (num, msg):
            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
        else:
Exemple #33
0
class cmd_drs_replicate(Command):
    """Replicate a naming context between two DCs."""

    synopsis = "%prog <destinationDC> <sourceDC> <NC> [options]"

    takes_optiongroups = {
        "sambaopts": options.SambaOptions,
        "versionopts": options.VersionOptions,
        "credopts": options.CredentialsOptions,
    }

    takes_args = ["DEST_DC", "SOURCE_DC", "NC"]

    takes_options = [
        Option("--add-ref", help="use ADD_REF to add to repsTo on source", action="store_true"),
        Option("--sync-forced", help="use SYNC_FORCED to force inbound replication", action="store_true"),
        Option("--sync-all", help="use SYNC_ALL to replicate from all DCs", action="store_true"),
        Option("--full-sync", help="resync all objects", action="store_true"),
        Option("--local", help="pull changes directly into the local database (destination DC is ignored)", action="store_true"),
        Option("--local-online", help="pull changes into the local database (destination DC is ignored) as a normal online replication", action="store_true"),
        Option("--async-op", help="use ASYNC_OP for the replication", action="store_true"),
        Option("--single-object", help="Replicate only the object specified, instead of the whole Naming Context (only with --local)", action="store_true"),
        ]

    def drs_local_replicate(self, SOURCE_DC, NC, full_sync=False,
                            single_object=False,
                            sync_forced=False):
        '''replicate from a source DC to the local SAM'''

        self.server = SOURCE_DC
        drsuapi_connect(self)

        self.local_samdb = SamDB(session_info=system_session(), url=None,
                                 credentials=self.creds, lp=self.lp)

        self.samdb = SamDB(url="ldap://%s" % self.server,
                           session_info=system_session(),
                           credentials=self.creds, lp=self.lp)

        # work out the source and destination GUIDs
        res = self.local_samdb.search(base="", scope=ldb.SCOPE_BASE,
                                      attrs=["dsServiceName"])
        self.ntds_dn = res[0]["dsServiceName"][0]

        res = self.local_samdb.search(base=self.ntds_dn, scope=ldb.SCOPE_BASE,
                                      attrs=["objectGUID"])
        self.ntds_guid = misc.GUID(
            self.samdb.schema_format_value("objectGUID",
                                           res[0]["objectGUID"][0]))

        source_dsa_invocation_id = misc.GUID(self.samdb.get_invocation_id())
        dest_dsa_invocation_id = misc.GUID(self.local_samdb.get_invocation_id())
        destination_dsa_guid = self.ntds_guid

        exop = drsuapi.DRSUAPI_EXOP_NONE

        if single_object:
            exop = drsuapi.DRSUAPI_EXOP_REPL_OBJ
            full_sync = True

        self.samdb.transaction_start()
        repl = drs_utils.drs_Replicate("ncacn_ip_tcp:%s[seal]" % self.server,
                                       self.lp,
                                       self.creds, self.local_samdb,
                                       dest_dsa_invocation_id)

        # Work out if we are an RODC, so that a forced local replicate
        # with the admin pw does not sync passwords
        rodc = self.local_samdb.am_rodc()
        try:
            (num_objects, num_links) = repl.replicate(NC,
                                                      source_dsa_invocation_id,
                                                      destination_dsa_guid,
                                                      rodc=rodc,
                                                      full_sync=full_sync,
                                                      exop=exop,
                                                      sync_forced=sync_forced)
        except Exception as e:
            raise CommandError("Error replicating DN %s" % NC, e)
        self.samdb.transaction_commit()

        if full_sync:
            self.message("Full Replication of all %d objects and %d links "
                         "from %s to %s was successful." %
                         (num_objects, num_links, SOURCE_DC,
                          self.local_samdb.url))
        else:
            self.message("Incremental replication of %d objects and %d links "
                         "from %s to %s was successful." %
                         (num_objects, num_links, SOURCE_DC,
                          self.local_samdb.url))

    def run(self, DEST_DC, SOURCE_DC, NC,
            add_ref=False, sync_forced=False, sync_all=False, full_sync=False,
            local=False, local_online=False, async_op=False, single_object=False,
            sambaopts=None, credopts=None, versionopts=None):

        self.server = DEST_DC
        self.lp = sambaopts.get_loadparm()

        self.creds = credopts.get_credentials(self.lp, fallback_machine=True)

        if local:
            self.drs_local_replicate(SOURCE_DC, NC, full_sync=full_sync,
                                     single_object=single_object,
                                     sync_forced=sync_forced)
            return

        if local_online:
            server_bind = drsuapi.drsuapi("irpc:dreplsrv", lp_ctx=self.lp)
            server_bind_handle = misc.policy_handle()
        else:
            drsuapi_connect(self)
            server_bind = self.drsuapi
            server_bind_handle = self.drsuapi_handle

        if not async_op:
            # Give the sync replication 5 minutes time
            server_bind.request_timeout = 5 * 60

        samdb_connect(self)

        # we need to find the NTDS GUID of the source DC
        msg = self.samdb.search(base=self.samdb.get_config_basedn(),
                                expression="(&(objectCategory=server)(|(name=%s)(dNSHostName=%s)))" % (
            ldb.binary_encode(SOURCE_DC),
            ldb.binary_encode(SOURCE_DC)),
                                attrs=[])
        if len(msg) == 0:
            raise CommandError("Failed to find source DC %s" % SOURCE_DC)
        server_dn = msg[0]['dn']

        msg = self.samdb.search(base=server_dn, scope=ldb.SCOPE_ONELEVEL,
                                expression="(|(objectCategory=nTDSDSA)(objectCategory=nTDSDSARO))",
                                attrs=['objectGUID', 'options'])
        if len(msg) == 0:
            raise CommandError("Failed to find source NTDS DN %s" % SOURCE_DC)
        source_dsa_guid = msg[0]['objectGUID'][0]
        dsa_options = int(attr_default(msg, 'options', 0))


        req_options = 0
        if not (dsa_options & dsdb.DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL):
            req_options |= drsuapi.DRSUAPI_DRS_WRIT_REP
        if add_ref:
            req_options |= drsuapi.DRSUAPI_DRS_ADD_REF
        if sync_forced:
            req_options |= drsuapi.DRSUAPI_DRS_SYNC_FORCED
        if sync_all:
            req_options |= drsuapi.DRSUAPI_DRS_SYNC_ALL
        if full_sync:
            req_options |= drsuapi.DRSUAPI_DRS_FULL_SYNC_NOW
        if async_op:
            req_options |= drsuapi.DRSUAPI_DRS_ASYNC_OP

        try:
            drs_utils.sendDsReplicaSync(server_bind, server_bind_handle, source_dsa_guid, NC, req_options)
        except drs_utils.drsException as estr:
            raise CommandError("DsReplicaSync failed", estr)
        if async_op:
            self.message("Replicate from %s to %s was started." % (SOURCE_DC, DEST_DC))
        else:
            self.message("Replicate from %s to %s was successful." % (SOURCE_DC, DEST_DC))
Exemple #34
0
class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):

    def setUp(self):
        super(AuthLogPassChangeTests, self).setUp()

        self.remoteAddress = os.environ["CLIENT_IP"]
        self.server_ip = os.environ["SERVER_IP"]

        host = "ldap://%s" % os.environ["SERVER"]
        self.ldb = SamDB(url=host,
                         session_info=system_session(),
                         credentials=self.get_credentials(),
                         lp=self.get_loadparm())

        print "ldb %s" % type(self.ldb)
        # Gets back the basedn
        base_dn = self.ldb.domain_dn()
        print "base_dn %s" % base_dn

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # Get the old "dSHeuristics" if it was set
        dsheuristics = self.ldb.get_dsheuristics()

        # Set the "dSHeuristics" to activate the correct "userPassword"
        # behaviour
        self.ldb.set_dsheuristics("000000001")

        # Reset the "dSHeuristics" as they were before
        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)

        # Get the old "minPwdAge"
        minPwdAge = self.ldb.get_minPwdAge()

        # Set it temporarily to "0"
        self.ldb.set_minPwdAge("0")
        self.base_dn = self.ldb.domain_dn()

        # Reset the "minPwdAge" as it was before
        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)

        # (Re)adds the test user USER_NAME with password USER_PASS
        delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": USER_NAME,
             "userPassword": USER_PASS
        })

        # discard any auth log messages for the password setup
        self.discardMessages()

    def tearDown(self):
        super(AuthLogPassChangeTests, self).tearDown()


    def test_admin_change_password(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_OK" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template = self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        net.change_password(newpassword=password.encode('utf-8'),
                            username=USER_NAME,
                            oldpassword=USER_PASS)


        messages = self.waitForMessages(isLastExpectedMessage)
        print "Received %d messages" % len(messages)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")

    def test_admin_change_password_new_password_fails_restriction(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_PASSWORD_RESTRICTION" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template=self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        exception_thrown = False
        try:
            net.change_password(newpassword=password.encode('utf-8'),
                                oldpassword=USER_PASS,
                                username=USER_NAME)
        except Exception as msg:
            exception_thrown = True
        self.assertEquals(True, exception_thrown,
                          "Expected exception not thrown")

        messages = self.waitForMessages(isLastExpectedMessage)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")

    def test_admin_change_password_unknown_user(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_NO_SUCH_USER" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template=self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        exception_thrown = False
        try:
            net.change_password(newpassword=password.encode('utf-8'),
                                oldpassword=USER_PASS,
                                username="******")
        except Exception as msg:
            exception_thrown = True
        self.assertEquals(True, exception_thrown,
                          "Expected exception not thrown")

        messages = self.waitForMessages(isLastExpectedMessage)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")

    def test_admin_change_password_bad_original_password(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_WRONG_PASSWORD" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template=self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        exception_thrown = False
        try:
            net.change_password(newpassword=password.encode('utf-8'),
                                oldpassword="******",
                                username=USER_NAME)
        except Exception as msg:
            exception_thrown = True
        self.assertEquals(True, exception_thrown,
                          "Expected exception not thrown")

        messages = self.waitForMessages(isLastExpectedMessage)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")

    # net rap password changes are broken, but they trigger enough of the
    # server side behaviour to exercise the code paths of interest.
    # if we used the real password it would be too long and does not hash
    # correctly, so we just check it triggers the wrong password path.
    def test_rap_change_password(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_WRONG_PASSWORD" and
                    msg["Authentication"]["authDescription"]
                        == "OemChangePasswordUser2")

        username = os.environ["USERNAME"]
        server = os.environ["SERVER"]
        password = os.environ["PASSWORD"]
        server_param = "--server=%s" % server
        creds = "-U%s%%%s" % (username,password)
        call(["bin/net", "rap", server_param,
              "password", USER_NAME, "notMyPassword", "notGoingToBeMyPassword",
              server, creds, "--option=client ipc max protocol=nt1"])

        messages = self.waitForMessages(isLastExpectedMessage)
        self.assertEquals(7,
                          len(messages),
                          "Did not receive the expected number of messages")

    def test_ldap_change_password(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_OK" and
                    msg["Authentication"]["serviceDescription"]
                        == "LDAP Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "LDAP Modify")

        new_password = samba.generate_random_password(32,32)
        self.ldb.modify_ldif(
            "dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" +
            "changetype: modify\n" +
            "delete: userPassword\n" +
            "userPassword: "******"\n" +
            "add: userPassword\n" +
            "userPassword: "******"\n"
        )

        messages = self.waitForMessages(isLastExpectedMessage)
        print "Received %d messages" % len(messages)
        self.assertEquals(4,
                          len(messages),
                          "Did not receive the expected number of messages")

    #
    # Currently this does not get logged, so we expect to only see the log
    # entries for the underlying ldap bind.
    #
    def test_ldap_change_password_bad_user(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authorization" and
                    msg["Authorization"]["serviceDescription"]
                        == "LDAP" and
                    msg["Authorization"]["authType"] == "krb5")

        new_password = samba.generate_random_password(32,32)
        try:
            self.ldb.modify_ldif(
                "dn: cn=" + "badUser" + ",cn=users," + self.base_dn + "\n" +
                "changetype: modify\n" +
                "delete: userPassword\n" +
                "userPassword: "******"\n" +
                "add: userPassword\n" +
                "userPassword: "******"\n"
            )
            self.fail()
        except LdbError, (num, msg):
            pass

        messages = self.waitForMessages(isLastExpectedMessage)
        print "Received %d messages" % len(messages)
        self.assertEquals(3,
                          len(messages),
                          "Did not receive the expected number of messages")
Exemple #35
0
def checkusage(names, lp, creds):
    """Checks whether this server is already provisioned and is being used.

    :param names: provision names object.
    :param lp: Loadparm context
    :param creds: Credentials Context
    """

    session_info = system_session()

    samdb = SamDB(url=get_ldb_url(lp, creds, names), session_info=session_info,
                  credentials=creds, lp=lp)

    try:
        config_dn = samdb.get_config_basedn()
        mapi_servers = samdb.search(
            base=config_dn, scope=ldb.SCOPE_SUBTREE,
            expression="(&(objectClass=msExchExchangeServer)(cn=%s))" % names.netbiosname)
        server_uses = []

        if len(mapi_servers) == 0:
            # The server is not provisioned.
            raise NotProvisionedError

        if len(mapi_servers) > 1:
            # Check if we are the primary folder store server.
            our_siteFolderName = "CN=Public Folder Store (%s),CN=First Storage Group,CN=InformationStore,CN=%s,CN=Servers,CN=%s,CN=AdministrativeGroups,%s" % (names.netbiosname, names.netbiosname, names.firstou, names.firstorgdn)
            dn = "CN=%s,CN=Administrative Groups,%s" % (names.firstou,
                                                        names.firstorgdn)
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['siteFolderServer'])
            assert len(ret) == 1
            siteFolderName = ret[0]["siteFolderServer"][0]
            if our_siteFolderName.lower() == siteFolderName.lower():
                server_uses.append("primary folder store server")

            # Check if we are the primary receipt update service
            our_addressListServiceLink = "CN=%s,CN=Servers,CN=%s,CN=Administrative Groups,%s" % (names.netbiosname, names.firstou, names.firstorgdn)
            dn = "CN=Recipient Update Service (%s),CN=Recipient Update Services,CN=Address Lists Container,%s" % (names.domain, names.firstorgdn)
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['msExchAddressListServiceLink'])
            assert len(ret) == 1
            addressListServiceLink = ret[0]['msExchAddressListServiceLink'][0]
            if our_addressListServiceLink.lower() == addressListServiceLink.lower():
                server_uses.append("primary receipt update service server")

        # Check if we handle any mailbox.
        db = Ldb(
            url=get_ldb_url(lp, creds, names), session_info=system_session(),
            credentials=creds, lp=lp)

        our_mailbox_store = "CN=Mailbox Store (%s),CN=First Storage Group,CN=InformationStore,CN=%s,CN=Servers,CN=%s,CN=Administrative Groups,%s" % (names.netbiosname, names.netbiosname, names.firstou, names.firstorgdn)
        mailboxes = db.search(
            base=names.domaindn, scope=ldb.SCOPE_SUBTREE,
            expression="(homeMDB=*)")
        mailboxes_handled = 0
        for user_mailbox in mailboxes:
            if (user_mailbox['homeMDB'][0] == our_mailbox_store and
                user_mailbox['msExchUserAccountControl'][0] != '2'):
                mailboxes_handled += 1

        if mailboxes_handled > 0:
            server_uses.append(
                "handling %d mailboxes" % mailboxes_handled)

        return server_uses
    except LdbError, ldb_error:
        print >> sys.stderr, "[!] error while checking whether this server is being used (%d): %s" % ldb_error.args
        raise ldb_error
Exemple #36
0
def checkusage(names, lp, creds):
    """Checks whether this server is already provisioned and is being used.

    :param names: provision names object.
    :param lp: Loadparm context
    :param creds: Credentials Context
    """

    session_info = system_session()

    samdb = SamDB(url=get_ldb_url(lp, creds, names), session_info=session_info,
                  credentials=creds, lp=lp)

    try:
        config_dn = samdb.get_config_basedn()
        mapi_servers = samdb.search(
            base=config_dn, scope=ldb.SCOPE_SUBTREE,
            expression="(&(objectClass=msExchExchangeServer)(cn=%s))" % names.netbiosname)
        server_uses = []

        if len(mapi_servers) == 0:
            # The server is not provisioned.
            raise NotProvisionedError

        if len(mapi_servers) > 1:
            # Check if we are the primary folder store server.
            our_siteFolderName = "CN=Public Folder Store (%s),CN=First Storage Group,CN=InformationStore,CN=%s,CN=Servers,CN=%s,CN=AdministrativeGroups,%s" % (names.netbiosname, names.netbiosname, names.firstou, names.firstorgdn)
            dn = "CN=%s,CN=Administrative Groups,%s" % (names.firstou,
                                                        names.firstorgdn)
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['siteFolderServer'])
            assert len(ret) == 1
            siteFolderName = ret[0]["siteFolderServer"][0]
            if our_siteFolderName.lower() == siteFolderName.lower():
                server_uses.append("primary folder store server")

            # Check if we are the primary receipt update service
            our_addressListServiceLink = "CN=%s,CN=Servers,CN=%s,CN=Administrative Groups,%s" % (names.netbiosname, names.firstou, names.firstorgdn)
            dn = "CN=Recipient Update Service (%s),CN=Recipient Update Services,CN=Address Lists Container,%s" % (names.domain, names.firstorgdn)
            ret = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['msExchAddressListServiceLink'])
            assert len(ret) == 1
            addressListServiceLink = ret[0]['msExchAddressListServiceLink'][0]
            if our_addressListServiceLink.lower() == addressListServiceLink.lower():
                server_uses.append("primary receipt update service server")

        # Check if we handle any mailbox.
        db = Ldb(
            url=get_ldb_url(lp, creds, names), session_info=system_session(),
            credentials=creds, lp=lp)

        our_mailbox_store = "CN=Mailbox Store (%s),CN=First Storage Group,CN=InformationStore,CN=%s,CN=Servers,CN=%s,CN=Administrative Groups,%s" % (names.netbiosname, names.netbiosname, names.firstou, names.firstorgdn)
        mailboxes = db.search(
            base=names.domaindn, scope=ldb.SCOPE_SUBTREE,
            expression="(homeMDB=*)")
        mailboxes_handled = 0
        for user_mailbox in mailboxes:
            if (user_mailbox['homeMDB'][0] == our_mailbox_store and
                user_mailbox['msExchUserAccountControl'][0] != '2'):
                mailboxes_handled += 1

        if mailboxes_handled > 0:
            server_uses.append(
                "handling %d mailboxes" % mailboxes_handled)

        return server_uses
    except LdbError, ldb_error:
        print >> sys.stderr, "[!] error while checking whether this server is being used (%d): %s" % ldb_error.args
        raise ldb_error
Exemple #37
0
class DsdbLockTestCase(SamDBTestCase):
    def test_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session, lp=self.lp)

            self.samdb.transaction_start()

            dn = "cn=test_db_lock_user,cn=users," + str(basedn)
            self.samdb.add({
                "dn": dn,
                "objectclass": "user",
            })
            self.samdb.delete(dn)

            # Obtain a write lock
            self.samdb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            self.samdb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        # Release the locks
        for l in res:
            pass

        end = time.time()
        self.assertGreater(end - start, 1.9)

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session, lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        dn = "cn=test_db_lock_user,cn=users," + str(basedn)
        self.samdb.add({
            "dn": dn,
            "objectclass": "user",
        })
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the parent releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            self.samdb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock3(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session, lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")

        # This will end up in the top level db
        dn = "@DSDB_LOCK_TEST"
        self.samdb.add({"dn": dn})
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        self.assertGreater(end - start, 1.9)
        os.write(w1, b"prepared")

        # Drop the write lock
        self.samdb.transaction_cancel()

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)
        self.assertEqual(got_pid, pid)

    def _test_full_db_lock1(self, backend_path):
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del (self.samdb)
            gc.collect()

            backenddb = ldb.Ldb(backend_path)

            backenddb.transaction_start()

            backenddb.add({"dn": "@DSDB_LOCK_TEST"})
            backenddb.delete("@DSDB_LOCK_TEST")

            # Obtain a write lock
            backenddb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            backenddb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        end = time.time()
        self.assertGreater(end - start, 1.9)

        # Release the locks
        for l in res:
            pass

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)

    def test_full_db_lock1_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)

    def _test_full_db_lock2(self, backend_path):
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:

            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session, lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)
            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # In the parent, close the main DB, re-open just one DB
        del (self.samdb)
        gc.collect()
        backenddb = ldb.Ldb(backend_path)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        backenddb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        backenddb.add({"dn": "@DSDB_LOCK_TEST"})
        backenddb.delete("@DSDB_LOCK_TEST")
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        backenddb.transaction_prepare_commit()
        end = time.time()

        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            backenddb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)

    def test_full_db_lock2_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)
Exemple #38
0
class PassWordHashTests(TestCase):

    def setUp(self):
        super(PassWordHashTests, self).setUp()

    # Add a user to ldb, this will exercise the password_hash code
    # and calculate the appropriate supplemental credentials
    def add_user(self, options=None, clear_text=False):
        self.lp = samba.tests.env_loadparm()
        # set any needed options
        if options is not None:
            for (option,value) in options:
                self.lp.set(option, value)

        self.creds = Credentials()
        self.session = system_session()
        self.ldb = SamDB(
            session_info=self.session,
            credentials=self.creds,
            lp=self.lp)

        # Gets back the basedn
        base_dn = self.ldb.domain_dn()

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # Get the old "dSHeuristics" if it was set
        dsheuristics = self.ldb.get_dsheuristics()

        # Set the "dSHeuristics" to activate the correct "userPassword"
        # behaviour
        self.ldb.set_dsheuristics("000000001")

        # Reset the "dSHeuristics" as they were before
        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)

        # Get the old "minPwdAge"
        minPwdAge = self.ldb.get_minPwdAge()

        # Set it temporarily to "0"
        self.ldb.set_minPwdAge("0")
        self.base_dn = self.ldb.domain_dn()

        # Reset the "minPwdAge" as it was before
        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)

        account_control = 0
        if clear_text:
            # get the current pwdProperties
            pwdProperties = self.ldb.get_pwdProperties()
            # enable clear text properties
            props = int(pwdProperties)
            props |= DOMAIN_PASSWORD_STORE_CLEARTEXT
            self.ldb.set_pwdProperties(str(props))
            # Restore the value on exit.
            self.addCleanup(self.ldb.set_pwdProperties, pwdProperties)
            account_control |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED

        # (Re)adds the test user USER_NAME with password USER_PASS
        # and userPrincipalName UPN
        delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": USER_NAME,
             "userPassword": USER_PASS,
             "userPrincipalName": UPN,
             "userAccountControl": str(account_control)
        })

    # Get the supplemental credentials for the user under test
    def get_supplemental_creds(self):
        base = "cn=" + USER_NAME + ",cn=users," + self.base_dn
        res = self.ldb.search(scope=ldb.SCOPE_BASE,
                              base=base,
                              attrs=["supplementalCredentials"])
        self.assertIs( True, len(res) > 0)
        obj = res[0]
        sc_blob = obj["supplementalCredentials"][0]
        sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob)
        return sc

    # Calculate and validate a Wdigest value
    def check_digest(self, user, realm, password,  digest):
        expected = calc_digest( user, realm, password)
        actual = binascii.hexlify(bytearray(digest))
        error = "Digest expected[%s], actual[%s], " \
                "user[%s], realm[%s], pass[%s]" % \
                (expected, actual, user, realm, password)
        self.assertEquals(expected, actual, error)

    # Check all of the 29 expected WDigest values
    #
    def check_wdigests(self, digests):

        self.assertEquals(29, digests.num_hashes)

        self.check_digest(USER_NAME,
                          self.lp.get("workgroup"),
                          USER_PASS,
                          digests.hashes[0].hash)
        self.check_digest(USER_NAME.lower(),
                          self.lp.get("workgroup").lower(),
                          USER_PASS,
                          digests.hashes[1].hash)
        self.check_digest(USER_NAME.upper(),
                          self.lp.get("workgroup").upper(),
                          USER_PASS,
                          digests.hashes[2].hash)
        self.check_digest(USER_NAME,
                          self.lp.get("workgroup").upper(),
                          USER_PASS,
                          digests.hashes[3].hash)
        self.check_digest(USER_NAME,
                          self.lp.get("workgroup").lower(),
                          USER_PASS,
                          digests.hashes[4].hash)
        self.check_digest(USER_NAME.upper(),
                          self.lp.get("workgroup").lower(),
                          USER_PASS,
                          digests.hashes[5].hash)
        self.check_digest(USER_NAME.lower(),
                          self.lp.get("workgroup").upper(),
                          USER_PASS,
                          digests.hashes[6].hash)
        self.check_digest(USER_NAME,
                          self.lp.get("realm").lower(),
                          USER_PASS,
                          digests.hashes[7].hash)
        self.check_digest(USER_NAME.lower(),
                          self.lp.get("realm").lower(),
                          USER_PASS,
                          digests.hashes[8].hash)
        self.check_digest(USER_NAME.upper(),
                          self.lp.get("realm"),
                          USER_PASS,
                          digests.hashes[9].hash)
        self.check_digest(USER_NAME,
                          self.lp.get("realm"),
                          USER_PASS,
                          digests.hashes[10].hash)
        self.check_digest(USER_NAME,
                          self.lp.get("realm").lower(),
                          USER_PASS,
                          digests.hashes[11].hash)
        self.check_digest(USER_NAME.upper(),
                          self.lp.get("realm").lower(),
                          USER_PASS,
                          digests.hashes[12].hash)
        self.check_digest(USER_NAME.lower(),
                          self.lp.get("realm"),
                          USER_PASS,
                          digests.hashes[13].hash)
        self.check_digest(UPN,
                          "",
                          USER_PASS,
                          digests.hashes[14].hash)
        self.check_digest(UPN.lower(),
                          "",
                          USER_PASS,
                          digests.hashes[15].hash)
        self.check_digest(UPN.upper(),
                          "",
                          USER_PASS,
                          digests.hashes[16].hash)

        name = "%s\\%s" % (self.lp.get("workgroup"), USER_NAME)
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[17].hash)

        name = "%s\\%s" % (self.lp.get("workgroup").lower(), USER_NAME.lower())
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[18].hash)

        name = "%s\\%s" % (self.lp.get("workgroup").upper(), USER_NAME.upper())
        self.check_digest(name,
                          "",
                          USER_PASS,
                          digests.hashes[19].hash)
        self.check_digest(USER_NAME,
                          "Digest",
                          USER_PASS,
                          digests.hashes[20].hash)
        self.check_digest(USER_NAME.lower(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[21].hash)
        self.check_digest(USER_NAME.upper(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[22].hash)
        self.check_digest(UPN,
                          "Digest",
                          USER_PASS,
                          digests.hashes[23].hash)
        self.check_digest(UPN.lower(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[24].hash)
        self.check_digest(UPN.upper(),
                          "Digest",
                          USER_PASS,
                          digests.hashes[25].hash)
        name = "%s\\%s" % (self.lp.get("workgroup"), USER_NAME)
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[26].hash)

        name = "%s\\%s" % (self.lp.get("workgroup").lower(), USER_NAME.lower())
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[27].hash)

        name = "%s\\%s" % (self.lp.get("workgroup").upper(), USER_NAME.upper())
        self.check_digest(name,
                          "Digest",
                          USER_PASS,
                          digests.hashes[28].hash)
Exemple #39
0
    try:
        # Query Partitions
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "objectSid",
                 "Enabled",
                 "systemFlags",
                 "dnsRoot",
                 "nCName",
                 "msDS-NC-Replica-Locations",
                 "msDS-NC-RO-Replica-Locations"]

        sstr = "CN=Partitions,{0!s}".format(samdb.get_config_basedn())
        res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRef)")

        # Write partitions output
        write_search_result(samdb, f, res)

        # Query cross reference container
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "fSMORoleOwner",
                 "systemFlags",
                 "msDS-Behavior-Version",
Exemple #40
0
class SiteCoverageTests(samba.tests.TestCase):
    def setUp(self):
        self.prefix = "kcc_"
        self.lp = samba.tests.env_loadparm()

        self.sites = {}
        self.site_links = {}

        self.creds = Credentials()
        self.creds.guess(self.lp)
        self.session = system_session()

        self.samdb = SamDB(session_info=self.session,
                           credentials=self.creds,
                           lp=self.lp)

    def tearDown(self):
        self.samdb.transaction_start()

        for site in self.sites:
            delete_force(self.samdb, site, controls=['tree_delete:1'])

        for site_link in self.site_links:
            delete_force(self.samdb, site_link)

        self.samdb.transaction_commit()

    def _add_server(self, name, site):
        dn = "CN={},CN=Servers,{}".format(name, site)
        self.samdb.add({
            "dn": dn,
            "objectClass": "server",
            "serverReference": self.samdb.domain_dn()
        })
        return dn

    def _add_site(self, name):
        dn = "CN={},CN=Sites,{}".format(name, self.samdb.get_config_basedn())
        self.samdb.add({"dn": dn, "objectClass": "site"})
        self.samdb.add({
            "dn": "CN=Servers," + dn,
            "objectClass": ["serversContainer"]
        })

        self.sites[dn] = name
        return dn, name.lower()

    def _add_site_link(self, name, links=[], cost=100):
        dn = "CN={},CN=IP,CN=Inter-Site Transports,CN=Sites,{}".format(
            name, self.samdb.get_config_basedn())
        self.samdb.add({
            "dn": dn,
            "objectClass": "siteLink",
            "cost": str(cost),
            "siteList": links
        })
        self.site_links[dn] = name
        return dn

    def test_single_site_link_same_dc_count(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)

        self._add_site_link(self.prefix + "link", [site1, site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_single_site_link_different_dc_count(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "ABCD" + '2', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)
        self._add_server(self.prefix + "BCDE" + '3', site2)

        self._add_site_link(self.prefix + "link", [site1, site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_two_site_links_same_cost(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "ABCD" + '2', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)
        self._add_server(self.prefix + "BCDE" + '3', site2)

        self._add_site_link(self.prefix + "link1", [site1, uncovered_dn])
        self._add_site_link(self.prefix + "link2", [site2, uncovered_dn])
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_two_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)

        self._add_site_link(self.prefix + "link1", [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2", [site2, uncovered_dn],
                            cost=75)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_three_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)

        self._add_site_link(self.prefix + "link1", [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2", [site2, uncovered_dn],
                            cost=75)
        self._add_site_link(self.prefix + "link3", [site3, uncovered_dn],
                            cost=60)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([], to_cover)

    def test_three_site_links_different_costs(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        uncovered_dn, uncovered = self._add_site(self.prefix + "uncovered")

        self._add_server(self.prefix + "ABCD" + '1', site1)
        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)

        self._add_site_link(self.prefix + "link1", [site1, uncovered_dn],
                            cost=50)
        self._add_site_link(self.prefix + "link2", [site2, uncovered_dn],
                            cost=75)
        self._add_site_link(self.prefix + "link3", [site3, uncovered_dn],
                            cost=50)
        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([uncovered], to_cover)

    def test_complex_setup_with_multiple_uncovered_sites(self):
        self.samdb.transaction_start()
        site1, name1 = self._add_site(self.prefix + "ABCD")
        site2, name2 = self._add_site(self.prefix + "BCDE")
        site3, name3 = self._add_site(self.prefix + "CDEF")

        site4, name4 = self._add_site(self.prefix + "1234")
        site5, name5 = self._add_site(self.prefix + "2345")
        site6, name6 = self._add_site(self.prefix + "3456")

        uncovered_dn1, uncovered1 = self._add_site(self.prefix + "uncovered1")
        uncovered_dn2, uncovered2 = self._add_site(self.prefix + "uncovered2")
        uncovered_dn3, uncovered3 = self._add_site(self.prefix + "uncovered3")

        # Site Link Cluster 1 - Server List
        self._add_server(self.prefix + "ABCD" + '1', site1)

        self._add_server(self.prefix + "BCDE" + '1', site2)
        self._add_server(self.prefix + "BCDE" + '2', site2)

        self._add_server(self.prefix + "CDEF" + '1', site3)
        self._add_server(self.prefix + "CDEF" + '2', site3)
        self._add_server(self.prefix + "CDEF" + '3', site3)

        # Site Link Cluster 2 - Server List
        self._add_server(self.prefix + "1234" + '1', site4)
        self._add_server(self.prefix + "1234" + '2', site4)

        self._add_server(self.prefix + "2345" + '1', site5)
        self._add_server(self.prefix + "2345" + '2', site5)

        self._add_server(self.prefix + "3456" + '1', site6)

        # Join to Uncovered1 (preference to site link cluster 1)
        self._add_site_link(self.prefix + "link1A",
                            [site1, site2, site3, uncovered_dn1],
                            cost=49)
        self._add_site_link(self.prefix + "link2A",
                            [site4, site5, site6, uncovered_dn1],
                            cost=50)

        # Join to Uncovered2 (no preferene on site links)
        self._add_site_link(self.prefix + "link1B",
                            [site1, site2, site3, uncovered_dn2],
                            cost=50)
        self._add_site_link(self.prefix + "link2B",
                            [site4, site5, site6, uncovered_dn2],
                            cost=50)

        # Join to Uncovered3 (preference to site link cluster 2)
        self._add_site_link(self.prefix + "link1C",
                            [site1, site2, site3, uncovered_dn3],
                            cost=50)
        self._add_site_link(self.prefix + "link2C",
                            [site4, site5, site6, uncovered_dn3],
                            cost=49)

        self.samdb.transaction_commit()

        to_cover = uncovered_sites_to_cover(self.samdb, name1)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name2)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name3)
        to_cover.sort()

        self.assertEqual([uncovered1, uncovered2], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name4)
        to_cover.sort()

        self.assertEqual([uncovered2, uncovered3], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name5)
        to_cover.sort()

        self.assertEqual([], to_cover)

        to_cover = uncovered_sites_to_cover(self.samdb, name6)
        to_cover.sort()

        self.assertEqual([], to_cover)

        for to_check in [uncovered1, uncovered2, uncovered3]:
            to_cover = uncovered_sites_to_cover(self.samdb, to_check)
            to_cover.sort()

            self.assertEqual([], to_cover)
Exemple #41
0
    def run(self, sambaopts=None, credopts=None,
            versionopts=None, server=None, targetdir=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        net = Net(creds, lp, server=credopts.ipaddress)

        netbios_name = lp.get("netbios name")
        samdb = SamDB(session_info=system_session(), credentials=creds, lp=lp)
        if not server:
            res = samdb.search(expression='(&(objectClass=computer)(serverReferenceBL=*))', attrs=["dnsHostName", "name"])
            if (len(res) == 0):
                raise CommandError("Unable to search for servers")

            if (len(res) == 1):
                raise CommandError("You are the latest server in the domain")

            server = None
            for e in res:
                if str(e["name"]).lower() != netbios_name.lower():
                    server = e["dnsHostName"]
                    break

        ntds_guid = samdb.get_ntds_GUID()
        msg = samdb.search(base=str(samdb.get_config_basedn()), scope=ldb.SCOPE_SUBTREE,
                                expression="(objectGUID=%s)" % ntds_guid,
                                attrs=['options'])
        if len(msg) == 0 or "options" not in msg[0]:
            raise CommandError("Failed to find options on %s" % ntds_guid)

        ntds_dn = msg[0].dn
        dsa_options = int(str(msg[0]['options']))

        res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
                            controls=["search_options:1:2"])

        if len(res) != 0:
            raise CommandError("Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC" % len(res))

        print "Using %s as partner server for the demotion" % server
        (drsuapiBind, drsuapi_handle, supportedExtensions) = drsuapi_connect(server, lp, creds)

        print "Desactivating inbound replication"

        nmsg = ldb.Message()
        nmsg.dn = msg[0].dn

        dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
        nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
        samdb.modify(nmsg)

        if not (dsa_options & DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():

            print "Asking partner server %s to synchronize from us" % server
            for part in (samdb.get_schema_basedn(),
                            samdb.get_config_basedn(),
                            samdb.get_root_basedn()):
                try:
                    sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid, str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
                except drsException, e:
                    print "Error while demoting, re-enabling inbound replication"
                    dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
                    nmsg["options"] = ldb.MessageElement(str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
                    samdb.modify(nmsg)
                    raise CommandError("Error while sending a DsReplicaSync for partion %s" % str(part), e)
Exemple #42
0
class DsdbLockTestCase(SamDBTestCase):
    def test_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del(self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               lp=self.lp)

            self.samdb.transaction_start()

            dn = "cn=test_db_lock_user,cn=users," + str(basedn)
            self.samdb.add({
                 "dn": dn,
                 "objectclass": "user",
            })
            self.samdb.delete(dn)

            # Obtain a write lock
            self.samdb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            self.samdb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        # Release the locks
        for l in res:
            pass

        end = time.time()
        self.assertGreater(end - start, 1.9)

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del(self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        dn = "cn=test_db_lock_user,cn=users," + str(basedn)
        self.samdb.add({
             "dn": dn,
             "objectclass": "user",
        })
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the parent releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            self.samdb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock3(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del(self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")

        # This will end up in the top level db
        dn = "@DSDB_LOCK_TEST"
        self.samdb.add({
             "dn": dn})
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        self.assertGreater(end - start, 1.9)
        os.write(w1, b"prepared")

        # Drop the write lock
        self.samdb.transaction_cancel()

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)
        self.assertEqual(got_pid, pid)


    def _test_full_db_lock1(self, backend_path):
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del(self.samdb)
            gc.collect()

            backenddb = ldb.Ldb(backend_path)


            backenddb.transaction_start()

            backenddb.add({"dn":"@DSDB_LOCK_TEST"})
            backenddb.delete("@DSDB_LOCK_TEST")

            # Obtain a write lock
            backenddb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            backenddb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        end = time.time()
        self.assertGreater(end - start, 1.9)

        # Release the locks
        for l in res:
            pass

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d",
                                       backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)


    def test_full_db_lock1_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d",
                                       backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)


    def _test_full_db_lock2(self, backend_path):
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:

            # In the child, close the main DB, re-open
            del(self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)
            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # In the parent, close the main DB, re-open just one DB
        del(self.samdb)
        gc.collect()
        backenddb = ldb.Ldb(backend_path)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        backenddb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        backenddb.add({"dn":"@DSDB_LOCK_TEST"})
        backenddb.delete("@DSDB_LOCK_TEST")
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        backenddb.transaction_prepare_commit()
        end = time.time()

        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            backenddb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d",
                                       backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)

    def test_full_db_lock2_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d",
                                       backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)
Exemple #43
0
    def run(self, subcommand, H=None, forest_level=None, domain_level=None,
            quiet=False, credopts=None, sambaopts=None, versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H, session_info=system_session(),
            credentials=creds, lp=lp)

        domain_dn = samdb.domain_dn()

        res_forest = samdb.search("CN=Partitions,%s" % samdb.get_config_basedn(),
          scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
        assert len(res_forest) == 1

        res_domain = samdb.search(domain_dn, scope=ldb.SCOPE_BASE,
          attrs=["msDS-Behavior-Version", "nTMixedDomain"])
        assert len(res_domain) == 1

        res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
          scope=ldb.SCOPE_SUBTREE, expression="(objectClass=nTDSDSA)",
          attrs=["msDS-Behavior-Version"])
        assert len(res_dc_s) >= 1

        try:
            level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
            level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
            level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])

            min_level_dc = int(res_dc_s[0]["msDS-Behavior-Version"][0]) # Init value
            for msg in res_dc_s:
                if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
                    min_level_dc = int(msg["msDS-Behavior-Version"][0])

            if level_forest < 0 or level_domain < 0:
                raise CommandError("Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!")
            if min_level_dc < 0:
                raise CommandError("Lowest function level of a DC is invalid. Correct this or reprovision!")
            if level_forest > level_domain:
                raise CommandError("Forest function level is higher than the domain level(s). Correct this or reprovision!")
            if level_domain > min_level_dc:
                raise CommandError("Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!")

        except KeyError:
            raise CommandError("Could not retrieve the actual domain, forest level and/or lowest DC function level!")

        if subcommand == "show":
            self.message("Domain and forest function level for domain '%s'" % domain_dn)
            if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message("\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
            if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message("\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!")
            if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message("\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!")

            self.message("")

            if level_forest == DS_DOMAIN_FUNCTION_2000:
                outstr = "2000"
            elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
                outstr = "2003 with mixed domains/interim (NT4 DC support)"
            elif level_forest == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif level_forest == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Forest function level: (Windows) " + outstr)

            if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                outstr = "2000 mixed (NT4 DC support)"
            elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
                outstr = "2000"
            elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
                outstr = "2003 with mixed domains/interim (NT4 DC support)"
            elif level_domain == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif level_domain == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Domain function level: (Windows) " + outstr)

            if min_level_dc == DS_DOMAIN_FUNCTION_2000:
                outstr = "2000"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Lowest function level of a DC: (Windows) " + outstr)

        elif subcommand == "raise":
            msgs = []

            if domain_level is not None:
                if domain_level == "2003":
                    new_level_domain = DS_DOMAIN_FUNCTION_2003
                elif domain_level == "2008":
                    new_level_domain = DS_DOMAIN_FUNCTION_2008
                elif domain_level == "2008_R2":
                    new_level_domain = DS_DOMAIN_FUNCTION_2008_R2

                if new_level_domain <= level_domain and level_domain_mixed == 0:
                    raise CommandError("Domain function level can't be smaller than or equal to the actual one!")

                if new_level_domain > min_level_dc:
                    raise CommandError("Domain function level can't be higher than the lowest function level of a DC!")

                # Deactivate mixed/interim domain support
                if level_domain_mixed != 0:
                    # Directly on the base DN
                    m = ldb.Message()
                    m.dn = ldb.Dn(samdb, domain_dn)
                    m["nTMixedDomain"] = ldb.MessageElement("0",
                      ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                    samdb.modify(m)
                    # Under partitions
                    m = ldb.Message()
                    m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup") + ",CN=Partitions,%s" % samdb.get_config_basedn())
                    m["nTMixedDomain"] = ldb.MessageElement("0",
                      ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                    try:
                        samdb.modify(m)
                    except ldb.LdbError, (enum, emsg):
                        if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                            raise

                # Directly on the base DN
                m = ldb.Message()
                m.dn = ldb.Dn(samdb, domain_dn)
                m["msDS-Behavior-Version"]= ldb.MessageElement(
                  str(new_level_domain), ldb.FLAG_MOD_REPLACE,
                            "msDS-Behavior-Version")
                samdb.modify(m)
                # Under partitions
                m = ldb.Message()
                m.dn = ldb.Dn(samdb, "CN=" + lp.get("workgroup")
                  + ",CN=Partitions,%s" % samdb.get_config_basedn())
                m["msDS-Behavior-Version"]= ldb.MessageElement(
                  str(new_level_domain), ldb.FLAG_MOD_REPLACE,
                          "msDS-Behavior-Version")
                try:
                    samdb.modify(m)
                except ldb.LdbError, (enum, emsg):
                    if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                        raise

                level_domain = new_level_domain
                msgs.append("Domain function level changed!")
Exemple #44
0
class SambaDnsUpdateTests(samba.tests.BlackboxTestCase):
    """Blackbox test case for samba_dnsupdate."""

    def setUp(self):
        self.server_ip = samba.tests.env_get_var_value("DNS_SERVER_IP")
        super(SambaDnsUpdateTests, self).setUp()
        try:
            out = self.check_output("samba_dnsupdate --verbose")
            self.assertTrue("Looking for DNS entry" in out, out)
        except samba.tests.BlackboxProcessError:
            pass

    def test_samba_dnsupate_no_change(self):
        try:
            out = self.check_output("samba_dnsupdate --verbose")
        except samba.tests.BlackboxProcessError as e:
            self.fail("Error calling samba_dnsupdate: %s" % e)
        self.assertTrue("No DNS updates needed" in out, out)

    def test_samba_dnsupate_set_ip(self):
        try:
            out = self.check_output("samba_dnsupdate --verbose --current-ip=10.0.0.1")
            self.assertTrue(" DNS updates and" in out, out)
            self.assertTrue(" DNS deletes needed" in out, out)
        except samba.tests.BlackboxProcessError:
            pass

        try:
            out = self.check_output("samba_dnsupdate --verbose --use-nsupdate --current-ip=10.0.0.1")
        except samba.tests.BlackboxProcessError as e:
            self.fail("Error calling samba_dnsupdate: %s" % e)

        self.assertTrue("No DNS updates needed" in out, out)
        try:
            rpc_out = self.check_output("samba_dnsupdate --verbose --use-samba-tool --rpc-server-ip=%s" % self.server_ip)
        except samba.tests.BlackboxProcessError as e:
            self.fail("Error calling samba_dnsupdate: %s" % e)

        self.assertTrue(" DNS updates and" in rpc_out, rpc_out)
        self.assertTrue(" DNS deletes needed" in rpc_out, rpc_out)
        out = self.check_output("samba_dnsupdate --verbose")
        self.assertTrue("No DNS updates needed" in out, out + rpc_out)

    def test_add_new_uncovered_site(self):
        name = 'sites'
        cmd = cmd_sambatool.subcommands[name]
        cmd.outf = StringIO()
        cmd.errf = StringIO()

        site_name = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

        # Clear out any existing site
        cmd._run("samba-tool %s" % name, 'remove', site_name)

        result = cmd._run("samba-tool %s" % name, 'create', site_name)
        if result is not None:
            self.fail("Error creating new site")

        self.lp = samba.tests.env_loadparm()
        self.creds = Credentials()
        self.creds.guess(self.lp)
        self.session = system_session()
        uc_fn = self.lp.private_path('dns_update_cache')
        tmp_uc = uc_fn + '_tmp'
        shutil.copyfile(uc_fn, tmp_uc)

        self.samdb = SamDB(session_info=self.session,
                           credentials=self.creds,
                           lp=self.lp)

        m = ldb.Message()
        m.dn = ldb.Dn(self.samdb, 'CN=DEFAULTIPSITELINK,CN=IP,'
                      'CN=Inter-Site Transports,CN=Sites,{}'.format(
                          self.samdb.get_config_basedn()))
        m['siteList'] = ldb.MessageElement("CN={},CN=Sites,{}".format(
            site_name,
            self.samdb.get_config_basedn()),
            ldb.FLAG_MOD_ADD, "siteList")

        dns_c = "samba_dnsupdate --verbose --use-file={}".format(tmp_uc)
        out = self.check_output(dns_c)
        self.assertFalse(site_name.lower() in out, out)

        self.samdb.modify(m)

        shutil.copyfile(uc_fn, tmp_uc)
        out = self.check_output(dns_c)

        self.assertFalse("No DNS updates needed" in out, out)
        self.assertTrue(site_name.lower() in out, out)

        result = cmd._run("samba-tool %s" % name, 'remove', site_name)
        if result is not None:
            self.fail("Error deleting site")
Exemple #45
0
class SambaDnsUpdateTests(samba.tests.BlackboxTestCase):
    """Blackbox test case for samba_dnsupdate."""

    def setUp(self):
        self.server_ip = samba.tests.env_get_var_value("DNS_SERVER_IP")
        super(SambaDnsUpdateTests, self).setUp()
        try:
            out = self.check_output("samba_dnsupdate --verbose")
            self.assertTrue("Looking for DNS entry" in out, out)
        except samba.tests.BlackboxProcessError:
            pass

    def test_samba_dnsupate_no_change(self):
        out = self.check_output("samba_dnsupdate --verbose")
        self.assertTrue("No DNS updates needed" in out, out)

    def test_samba_dnsupate_set_ip(self):
        try:
            out = self.check_output("samba_dnsupdate --verbose --current-ip=10.0.0.1")
            self.assertTrue(" DNS updates and" in out, out)
            self.assertTrue(" DNS deletes needed" in out, out)
        except samba.tests.BlackboxProcessError:
            pass

        try:
            out = self.check_output("samba_dnsupdate --verbose --use-nsupdate --current-ip=10.0.0.1")
        except samba.tests.BlackboxProcessError as e:
            self.fail("Error calling samba_dnsupdate: %s" % e)

        self.assertTrue("No DNS updates needed" in out, out)
        try:
            rpc_out = self.check_output("samba_dnsupdate --verbose --use-samba-tool --rpc-server-ip=%s" % self.server_ip)
        except samba.tests.BlackboxProcessError as e:
            self.fail("Error calling samba_dnsupdate: %s" % e)

        self.assertTrue(" DNS updates and" in rpc_out, rpc_out)
        self.assertTrue(" DNS deletes needed" in rpc_out, rpc_out)
        out = self.check_output("samba_dnsupdate --verbose")
        self.assertTrue("No DNS updates needed" in out, out + rpc_out)

    def test_add_new_uncovered_site(self):
        name = 'sites'
        cmd = cmd_sambatool.subcommands[name]
        cmd.outf = StringIO()
        cmd.errf = StringIO()

        site_name = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

        # Clear out any existing site
        cmd._run("samba-tool %s" % name, 'remove', site_name)

        result = cmd._run("samba-tool %s" % name, 'create', site_name)
        if result is not None:
            self.fail("Error creating new site")

        self.lp = samba.tests.env_loadparm()
        self.creds = Credentials()
        self.creds.guess(self.lp)
        self.session = system_session()

        self.samdb = SamDB(session_info=self.session,
                           credentials=self.creds,
                           lp=self.lp)

        m = ldb.Message()
        m.dn = ldb.Dn(self.samdb, 'CN=DEFAULTIPSITELINK,CN=IP,'
                      'CN=Inter-Site Transports,CN=Sites,{}'.format(
                          self.samdb.get_config_basedn()))
        m['siteList'] = ldb.MessageElement("CN={},CN=Sites,{}".format(
            site_name,
            self.samdb.get_config_basedn()),
            ldb.FLAG_MOD_ADD, "siteList")

        out = self.check_output("samba_dnsupdate --verbose")
        self.assertTrue("No DNS updates needed" in out, out)

        self.samdb.modify(m)

        out = self.check_output("samba_dnsupdate --verbose --use-samba-tool"
                                " --rpc-server-ip={}".format(self.server_ip))

        self.assertFalse("No DNS updates needed" in out, out)
        self.assertTrue(site_name.lower() in out, out)

        result = cmd._run("samba-tool %s" % name, 'remove', site_name)
        if result is not None:
            self.fail("Error deleting site")
Exemple #46
0
    try:
        # Query Partitions
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "objectSid",
                 "Enabled",
                 "systemFlags",
                 "dnsRoot",
                 "nCName",
                 "msDS-NC-Replica-Locations",
                 "msDS-NC-RO-Replica-Locations"]

        sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
        res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRef)")

        # Write partitions output
        write_search_result(samdb, f, res)

        # Query cross reference container
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "fSMORoleOwner",
                 "systemFlags",
                 "msDS-Behavior-Version",
Exemple #47
0
def samdb_to_ldif_file(samdb, dburl, lp, creds, ldif_file):
    """Routine to extract all objects and attributes that are relevent
    to the KCC algorithms from a DC database.

    The point of this function is to allow a programmer/debugger to
    extract an LDIF file with non-security relevent information from
    a DC database.  The LDIF file can then be used to "import" via
    the import_ldif() function this file into 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: LDAP database URL to extract info from
    :param ldif_file: output LDIF file name to create
    """
    try:
        samdb = SamDB(url=dburl,
                      session_info=system_session(),
                      credentials=creds, lp=lp)
    except ldb.LdbError as e:
        (enum, estr) = e.args
        raise LdifError("Unable to open sam database (%s) : %s" %
                        (dburl, estr))

    if os.path.exists(ldif_file):
        raise LdifError("Specify a file (%s) that doesn't already exist." %
                        ldif_file)

    try:
        f = open(ldif_file, "w")
    except IOError as ioerr:
        raise LdifError("Unable to open (%s) : %s" % (ldif_file, str(ioerr)))

    try:
        # Query Partitions
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "objectSid",
                 "Enabled",
                 "systemFlags",
                 "dnsRoot",
                 "nCName",
                 "msDS-NC-Replica-Locations",
                 "msDS-NC-RO-Replica-Locations"]

        sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
        res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRef)")

        # Write partitions output
        write_search_result(samdb, f, res)

        # Query cross reference container
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "fSMORoleOwner",
                 "systemFlags",
                 "msDS-Behavior-Version",
                 "msDS-EnabledFeature"]

        sstr = "CN=Partitions,%s" % samdb.get_config_basedn()
        res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=crossRefContainer)")

        # Write cross reference container output
        write_search_result(samdb, f, res)

        # Query Sites
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "systemFlags"]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        sites = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                             attrs=attrs,
                             expression="(objectClass=site)")

        # Write sites output
        write_search_result(samdb, f, sites)

        # Query NTDS Site Settings
        for msg in sites:
            sitestr = str(msg.dn)

            attrs = ["objectClass",
                     "objectGUID",
                     "cn",
                     "whenChanged",
                     "interSiteTopologyGenerator",
                     "interSiteTopologyFailover",
                     "schedule",
                     "options"]

            sstr = "CN=NTDS Site Settings,%s" % sitestr
            res = samdb.search(base=sstr, scope=ldb.SCOPE_BASE,
                               attrs=attrs)

            # Write Site Settings output
            write_search_result(samdb, f, res)

        # Naming context list
        nclist = []

        # Query Directory Service Agents
        for msg in sites:
            sstr = str(msg.dn)

            ncattrs = ["hasMasterNCs",
                       "msDS-hasMasterNCs",
                       "hasPartialReplicaNCs",
                       "msDS-HasDomainNCs",
                       "msDS-hasFullReplicaNCs",
                       "msDS-HasInstantiatedNCs"]
            attrs = ["objectClass",
                     "objectGUID",
                     "cn",
                     "whenChanged",
                     "invocationID",
                     "options",
                     "msDS-isRODC",
                     "msDS-Behavior-Version"]

            res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                               attrs=attrs + ncattrs,
                               expression="(objectClass=nTDSDSA)")

            # Spin thru all the DSAs looking for NC replicas
            # and build a list of all possible Naming Contexts
            # for subsequent retrieval below
            for msg in res:
                for k in msg.keys():
                    if k in ncattrs:
                        for value in msg[k]:
                            # Some of these have binary DNs so
                            # use dsdb_Dn to split out relevent parts
                            dsdn = dsdb_Dn(samdb, value)
                            dnstr = str(dsdn.dn)
                            if dnstr not in nclist:
                                nclist.append(dnstr)

            # Write DSA output
            write_search_result(samdb, f, res)

        # Query NTDS Connections
        for msg in sites:
            sstr = str(msg.dn)

            attrs = ["objectClass",
                     "objectGUID",
                     "cn",
                     "whenChanged",
                     "options",
                     "whenCreated",
                     "enabledConnection",
                     "schedule",
                     "transportType",
                     "fromServer",
                     "systemFlags"]

            res = samdb.search(base=sstr, scope=ldb.SCOPE_SUBTREE,
                               attrs=attrs,
                               expression="(objectClass=nTDSConnection)")
            # Write NTDS Connection output
            write_search_result(samdb, f, res)

        # Query Intersite transports
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "options",
                 "name",
                 "bridgeheadServerListBL",
                 "transportAddressAttribute"]

        sstr = "CN=Inter-Site Transports,CN=Sites,%s" % \
               samdb.get_config_basedn()
        res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=interSiteTransport)")

        # Write inter-site transport output
        write_search_result(samdb, f, res)

        # Query siteLink
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "systemFlags",
                 "options",
                 "schedule",
                 "replInterval",
                 "siteList",
                 "cost"]

        sstr = "CN=Sites,%s" % \
               samdb.get_config_basedn()
        res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=siteLink)",
                           controls=['extended_dn:0'])

        # Write siteLink output
        write_search_result(samdb, f, res)

        # Query siteLinkBridge
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "siteLinkList"]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=siteLinkBridge)")

        # Write siteLinkBridge output
        write_search_result(samdb, f, res)

        # Query servers containers
        # Needed for samdb.server_site_name()
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "systemFlags"]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=serversContainer)")

        # Write servers container output
        write_search_result(samdb, f, res)

        # Query servers
        # Needed because some transport interfaces refer back to
        # attributes found in the server object.   Also needed
        # so extended-dn will be happy with dsServiceName in rootDSE
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "systemFlags",
                 "dNSHostName",
                 "mailAddress"]

        sstr = "CN=Sites,%s" % samdb.get_config_basedn()
        res = samdb.search(sstr, scope=ldb.SCOPE_SUBTREE,
                           attrs=attrs,
                           expression="(objectClass=server)")

        # Write server output
        write_search_result(samdb, f, res)

        # Query Naming Context replicas
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "objectSid",
                 "fSMORoleOwner",
                 "msDS-Behavior-Version",
                 "repsFrom",
                 "repsTo"]

        for sstr in nclist:
            res = samdb.search(sstr, scope=ldb.SCOPE_BASE,
                               attrs=attrs)

            # Write naming context output
            write_search_result(samdb, f, res)

        # Query rootDSE replicas
        attrs = ["objectClass",
                 "objectGUID",
                 "cn",
                 "whenChanged",
                 "rootDomainNamingContext",
                 "configurationNamingContext",
                 "schemaNamingContext",
                 "defaultNamingContext",
                 "dsServiceName"]

        sstr = ""
        res = samdb.search(sstr, scope=ldb.SCOPE_BASE,
                           attrs=attrs)

        # Record the rootDSE object as a dn as it
        # would appear in the base ldb file.  We have
        # to save it this way because we are going to
        # be importing as an abbreviated database.
        res[0].dn = ldb.Dn(samdb, "@ROOTDSE")

        # Write rootdse output
        write_search_result(samdb, f, res)

    except ldb.LdbError as e:
        (enum, estr) = e.args
        raise LdifError("Error processing (%s) : %s" % (sstr, estr))

    f.close()
Exemple #48
0
class DsdbTests(TestCase):
    def setUp(self):
        super(DsdbTests, self).setUp()
        self.lp = samba.tests.env_loadparm()
        self.creds = Credentials()
        self.creds.guess(self.lp)
        self.session = system_session()
        self.samdb = SamDB(session_info=self.session,
                           credentials=self.creds,
                           lp=self.lp)

    def test_get_oid_from_attrid(self):
        oid = self.samdb.get_oid_from_attid(591614)
        self.assertEquals(oid, "1.2.840.113556.1.4.1790")

    def test_error_replpropertymetadata(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["replPropertyMetaData"])
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
                          str(res[0]["replPropertyMetaData"]))
        ctr = repl.ctr
        for o in ctr.array:
            # Search for Description
            if o.attid == 13:
                old_version = o.version
                o.version = o.version + 1
        replBlob = ndr_pack(repl)
        msg = ldb.Message()
        msg.dn = res[0].dn
        msg["replPropertyMetaData"] = ldb.MessageElement(
            replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
        self.assertRaises(ldb.LdbError, self.samdb.modify, msg,
                          ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])

    def test_error_replpropertymetadata_nochange(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["replPropertyMetaData"])
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
                          str(res[0]["replPropertyMetaData"]))
        replBlob = ndr_pack(repl)
        msg = ldb.Message()
        msg.dn = res[0].dn
        msg["replPropertyMetaData"] = ldb.MessageElement(
            replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
        self.assertRaises(ldb.LdbError, self.samdb.modify, msg,
                          ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])

    def test_error_replpropertymetadata_allow_sort(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["replPropertyMetaData"])
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
                          str(res[0]["replPropertyMetaData"]))
        replBlob = ndr_pack(repl)
        msg = ldb.Message()
        msg.dn = res[0].dn
        msg["replPropertyMetaData"] = ldb.MessageElement(
            replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
        self.samdb.modify(msg, [
            "local_oid:1.3.6.1.4.1.7165.4.3.14:0",
            "local_oid:1.3.6.1.4.1.7165.4.3.25:0"
        ])

    def test_twoatt_replpropertymetadata(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["replPropertyMetaData", "uSNChanged"])
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
                          str(res[0]["replPropertyMetaData"]))
        ctr = repl.ctr
        for o in ctr.array:
            # Search for Description
            if o.attid == 13:
                old_version = o.version
                o.version = o.version + 1
                o.local_usn = long(str(res[0]["uSNChanged"])) + 1
        replBlob = ndr_pack(repl)
        msg = ldb.Message()
        msg.dn = res[0].dn
        msg["replPropertyMetaData"] = ldb.MessageElement(
            replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
        msg["description"] = ldb.MessageElement("new val",
                                                ldb.FLAG_MOD_REPLACE,
                                                "description")
        self.assertRaises(ldb.LdbError, self.samdb.modify, msg,
                          ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])

    def test_set_replpropertymetadata(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["replPropertyMetaData", "uSNChanged"])
        repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
                          str(res[0]["replPropertyMetaData"]))
        ctr = repl.ctr
        for o in ctr.array:
            # Search for Description
            if o.attid == 13:
                old_version = o.version
                o.version = o.version + 1
                o.local_usn = long(str(res[0]["uSNChanged"])) + 1
                o.originating_usn = long(str(res[0]["uSNChanged"])) + 1
        replBlob = ndr_pack(repl)
        msg = ldb.Message()
        msg.dn = res[0].dn
        msg["replPropertyMetaData"] = ldb.MessageElement(
            replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData")
        self.samdb.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])

    def test_ok_get_attribute_from_attid(self):
        self.assertEquals(self.samdb.get_attribute_from_attid(13),
                          "description")

    def test_ko_get_attribute_from_attid(self):
        self.assertEquals(self.samdb.get_attribute_from_attid(11979), None)

    def test_get_attribute_replmetadata_version(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["dn"])
        self.assertEquals(len(res), 1)
        dn = str(res[0].dn)
        self.assertEqual(
            self.samdb.get_attribute_replmetadata_version(dn, "unicodePwd"), 1)

    def test_set_attribute_replmetadata_version(self):
        res = self.samdb.search(expression="cn=Administrator",
                                scope=ldb.SCOPE_SUBTREE,
                                attrs=["dn"])
        self.assertEquals(len(res), 1)
        dn = str(res[0].dn)
        version = self.samdb.get_attribute_replmetadata_version(
            dn, "description")
        self.samdb.set_attribute_replmetadata_version(dn, "description",
                                                      version + 2)
        self.assertEqual(
            self.samdb.get_attribute_replmetadata_version(dn, "description"),
            version + 2)

    def test_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               credentials=self.creds,
                               lp=self.lp)

            self.samdb.transaction_start()

            dn = "cn=test_db_lock_user,cn=users," + str(basedn)
            self.samdb.add({
                "dn": dn,
                "objectclass": "user",
            })
            self.samdb.delete(dn)

            # Obtain a write lock
            self.samdb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            self.samdb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        # Release the locks
        for l in res:
            pass

        end = time.time()
        self.assertGreater(end - start, 1.9)

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               credentials=self.creds,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        dn = "cn=test_db_lock_user,cn=users," + str(basedn)
        self.samdb.add({
            "dn": dn,
            "objectclass": "user",
        })
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the parent releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            self.samdb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_db_lock3(self):
        basedn = self.samdb.get_default_basedn()
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               credentials=self.creds,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)

            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        self.samdb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")

        # This will end up in the top level db
        dn = "@DSDB_LOCK_TEST"
        self.samdb.add({"dn": dn})
        self.samdb.delete(dn)
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        self.samdb.transaction_prepare_commit()
        end = time.time()
        self.assertGreater(end - start, 1.9)
        os.write(w1, b"prepared")

        # Drop the write lock
        self.samdb.transaction_cancel()

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)
        self.assertEqual(got_pid, pid)

    def _test_full_db_lock1(self, backend_path):
        (r1, w1) = os.pipe()

        pid = os.fork()
        if pid == 0:
            # In the child, close the main DB, re-open just one DB
            del (self.samdb)
            gc.collect()

            backenddb = ldb.Ldb(backend_path)

            backenddb.transaction_start()

            backenddb.add({"dn": "@DSDB_LOCK_TEST"})
            backenddb.delete("@DSDB_LOCK_TEST")

            # Obtain a write lock
            backenddb.transaction_prepare_commit()
            os.write(w1, b"prepared")
            time.sleep(2)

            # Drop the write lock
            backenddb.transaction_cancel()
            os._exit(0)

        self.assertEqual(os.read(r1, 8), b"prepared")

        start = time.time()

        # We need to hold this iterator open to hold the all-record lock.
        res = self.samdb.search_iterator()

        # This should take at least 2 seconds because the transaction
        # has a write lock on one backend db open

        end = time.time()
        self.assertGreater(end - start, 1.9)

        # Release the locks
        for l in res:
            pass

        (got_pid, status) = os.waitpid(pid, 0)
        self.assertEqual(got_pid, pid)
        self.assertTrue(os.WIFEXITED(status))
        self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock1(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)

    def test_full_db_lock1_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock1(backend_path)

    def _test_full_db_lock2(self, backend_path):
        (r1, w1) = os.pipe()
        (r2, w2) = os.pipe()

        pid = os.fork()
        if pid == 0:

            # In the child, close the main DB, re-open
            del (self.samdb)
            gc.collect()
            self.samdb = SamDB(session_info=self.session,
                               credentials=self.creds,
                               lp=self.lp)

            # We need to hold this iterator open to hold the all-record lock.
            res = self.samdb.search_iterator()

            os.write(w2, b"start")
            if (os.read(r1, 7) != b"started"):
                os._exit(1)
            os.write(w2, b"add")
            if (os.read(r1, 5) != b"added"):
                os._exit(2)

            # Wait 2 seconds to block prepare_commit() in the child.
            os.write(w2, b"prepare")
            time.sleep(2)

            # Release the locks
            for l in res:
                pass

            if (os.read(r1, 8) != b"prepared"):
                os._exit(3)

            os._exit(0)

        # In the parent, close the main DB, re-open just one DB
        del (self.samdb)
        gc.collect()
        backenddb = ldb.Ldb(backend_path)

        # We can start the transaction during the search
        # because both just grab the all-record read lock.
        self.assertEqual(os.read(r2, 5), b"start")
        backenddb.transaction_start()
        os.write(w1, b"started")

        self.assertEqual(os.read(r2, 3), b"add")
        backenddb.add({"dn": "@DSDB_LOCK_TEST"})
        backenddb.delete("@DSDB_LOCK_TEST")
        os.write(w1, b"added")

        # Obtain a write lock, this will block until
        # the child releases the read lock.
        self.assertEqual(os.read(r2, 7), b"prepare")
        start = time.time()
        backenddb.transaction_prepare_commit()
        end = time.time()

        try:
            self.assertGreater(end - start, 1.9)
        except:
            raise
        finally:
            os.write(w1, b"prepared")

            # Drop the write lock
            backenddb.transaction_cancel()

            (got_pid, status) = os.waitpid(pid, 0)
            self.assertEqual(got_pid, pid)
            self.assertTrue(os.WIFEXITED(status))
            self.assertEqual(os.WEXITSTATUS(status), 0)

    def test_full_db_lock2(self):
        basedn = self.samdb.get_default_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)

    def test_full_db_lock2_config(self):
        basedn = self.samdb.get_config_basedn()
        backend_filename = "%s.ldb" % basedn.get_casefold()
        backend_subpath = os.path.join("sam.ldb.d", backend_filename)
        backend_path = self.lp.private_path(backend_subpath)
        self._test_full_db_lock2(backend_path)

    def test_no_error_on_invalid_control(self):
        try:
            res = self.samdb.search(
                expression="cn=Administrator",
                scope=ldb.SCOPE_SUBTREE,
                attrs=["replPropertyMetaData"],
                controls=[
                    "local_oid:%s:0" %
                    dsdb.DSDB_CONTROL_INVALID_NOT_IMPLEMENTED
                ])
        except ldb.LdbError as e:
            self.fail("Should have not raised an exception")

    def test_error_on_invalid_critical_control(self):
        try:
            res = self.samdb.search(
                expression="cn=Administrator",
                scope=ldb.SCOPE_SUBTREE,
                attrs=["replPropertyMetaData"],
                controls=[
                    "local_oid:%s:1" %
                    dsdb.DSDB_CONTROL_INVALID_NOT_IMPLEMENTED
                ])
        except ldb.LdbError as e:
            if e[0] != ldb.ERR_UNSUPPORTED_CRITICAL_EXTENSION:
                self.fail(
                    "Got %s should have got ERR_UNSUPPORTED_CRITICAL_EXTENSION"
                    % e[1])
Exemple #49
0
    def run(self,
            sambaopts=None,
            credopts=None,
            versionopts=None,
            server=None,
            targetdir=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        net = Net(creds, lp, server=credopts.ipaddress)

        netbios_name = lp.get("netbios name")
        samdb = SamDB(session_info=system_session(), credentials=creds, lp=lp)
        if not server:
            res = samdb.search(
                expression='(&(objectClass=computer)(serverReferenceBL=*))',
                attrs=["dnsHostName", "name"])
            if (len(res) == 0):
                raise CommandError("Unable to search for servers")

            if (len(res) == 1):
                raise CommandError("You are the latest server in the domain")

            server = None
            for e in res:
                if str(e["name"]).lower() != netbios_name.lower():
                    server = e["dnsHostName"]
                    break

        ntds_guid = samdb.get_ntds_GUID()
        msg = samdb.search(base=str(samdb.get_config_basedn()),
                           scope=ldb.SCOPE_SUBTREE,
                           expression="(objectGUID=%s)" % ntds_guid,
                           attrs=['options'])
        if len(msg) == 0 or "options" not in msg[0]:
            raise CommandError("Failed to find options on %s" % ntds_guid)

        ntds_dn = msg[0].dn
        dsa_options = int(str(msg[0]['options']))

        res = samdb.search(expression="(fSMORoleOwner=%s)" % str(ntds_dn),
                           controls=["search_options:1:2"])

        if len(res) != 0:
            raise CommandError(
                "Current DC is still the owner of %d role(s), use the role command to transfer roles to another DC"
            )

        print "Using %s as partner server for the demotion" % server
        (drsuapiBind, drsuapi_handle,
         supportedExtensions) = drsuapi_connect(server, lp, creds)

        print "Desactivating inbound replication"

        nmsg = ldb.Message()
        nmsg.dn = msg[0].dn

        dsa_options |= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
        nmsg["options"] = ldb.MessageElement(str(dsa_options),
                                             ldb.FLAG_MOD_REPLACE, "options")
        samdb.modify(nmsg)

        if not (dsa_options &
                DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL) and not samdb.am_rodc():

            print "Asking partner server %s to synchronize from us" % server
            for part in (samdb.get_schema_basedn(), samdb.get_config_basedn(),
                         samdb.get_root_basedn()):
                try:
                    sendDsReplicaSync(drsuapiBind, drsuapi_handle, ntds_guid,
                                      str(part), drsuapi.DRSUAPI_DRS_WRIT_REP)
                except drsException, e:
                    print "Error while demoting, re-enabling inbound replication"
                    dsa_options ^= DS_NTDSDSA_OPT_DISABLE_INBOUND_REPL
                    nmsg["options"] = ldb.MessageElement(
                        str(dsa_options), ldb.FLAG_MOD_REPLACE, "options")
                    samdb.modify(nmsg)
                    raise CommandError(
                        "Error while sending a DsReplicaSync for partion %s" %
                        str(part), e)
Exemple #50
0
    def run(self,
            subcommand,
            H=None,
            forest_level=None,
            domain_level=None,
            quiet=False,
            credopts=None,
            sambaopts=None,
            versionopts=None):
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp, fallback_machine=True)

        samdb = SamDB(url=H,
                      session_info=system_session(),
                      credentials=creds,
                      lp=lp)

        domain_dn = samdb.domain_dn()

        res_forest = samdb.search("CN=Partitions,%s" %
                                  samdb.get_config_basedn(),
                                  scope=ldb.SCOPE_BASE,
                                  attrs=["msDS-Behavior-Version"])
        assert len(res_forest) == 1

        res_domain = samdb.search(
            domain_dn,
            scope=ldb.SCOPE_BASE,
            attrs=["msDS-Behavior-Version", "nTMixedDomain"])
        assert len(res_domain) == 1

        res_dc_s = samdb.search("CN=Sites,%s" % samdb.get_config_basedn(),
                                scope=ldb.SCOPE_SUBTREE,
                                expression="(objectClass=nTDSDSA)",
                                attrs=["msDS-Behavior-Version"])
        assert len(res_dc_s) >= 1

        try:
            level_forest = int(res_forest[0]["msDS-Behavior-Version"][0])
            level_domain = int(res_domain[0]["msDS-Behavior-Version"][0])
            level_domain_mixed = int(res_domain[0]["nTMixedDomain"][0])

            min_level_dc = int(
                res_dc_s[0]["msDS-Behavior-Version"][0])  # Init value
            for msg in res_dc_s:
                if int(msg["msDS-Behavior-Version"][0]) < min_level_dc:
                    min_level_dc = int(msg["msDS-Behavior-Version"][0])

            if level_forest < 0 or level_domain < 0:
                raise CommandError(
                    "Domain and/or forest function level(s) is/are invalid. Correct them or reprovision!"
                )
            if min_level_dc < 0:
                raise CommandError(
                    "Lowest function level of a DC is invalid. Correct this or reprovision!"
                )
            if level_forest > level_domain:
                raise CommandError(
                    "Forest function level is higher than the domain level(s). Correct this or reprovision!"
                )
            if level_domain > min_level_dc:
                raise CommandError(
                    "Domain function level is higher than the lowest function level of a DC. Correct this or reprovision!"
                )

        except KeyError:
            raise CommandError(
                "Could not retrieve the actual domain, forest level and/or lowest DC function level!"
            )

        if subcommand == "show":
            self.message("Domain and forest function level for domain '%s'" %
                         domain_dn)
            if level_forest == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message(
                    "\nATTENTION: You run SAMBA 4 on a forest function level lower than Windows 2000 (Native). This isn't supported! Please raise!"
                )
            if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message(
                    "\nATTENTION: You run SAMBA 4 on a domain function level lower than Windows 2000 (Native). This isn't supported! Please raise!"
                )
            if min_level_dc == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                self.message(
                    "\nATTENTION: You run SAMBA 4 on a lowest function level of a DC lower than Windows 2003. This isn't supported! Please step-up or upgrade the concerning DC(s)!"
                )

            self.message("")

            if level_forest == DS_DOMAIN_FUNCTION_2000:
                outstr = "2000"
            elif level_forest == DS_DOMAIN_FUNCTION_2003_MIXED:
                outstr = "2003 with mixed domains/interim (NT4 DC support)"
            elif level_forest == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif level_forest == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif level_forest == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Forest function level: (Windows) " + outstr)

            if level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed != 0:
                outstr = "2000 mixed (NT4 DC support)"
            elif level_domain == DS_DOMAIN_FUNCTION_2000 and level_domain_mixed == 0:
                outstr = "2000"
            elif level_domain == DS_DOMAIN_FUNCTION_2003_MIXED:
                outstr = "2003 with mixed domains/interim (NT4 DC support)"
            elif level_domain == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif level_domain == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif level_domain == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Domain function level: (Windows) " + outstr)

            if min_level_dc == DS_DOMAIN_FUNCTION_2000:
                outstr = "2000"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2003:
                outstr = "2003"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2008:
                outstr = "2008"
            elif min_level_dc == DS_DOMAIN_FUNCTION_2008_R2:
                outstr = "2008 R2"
            else:
                outstr = "higher than 2008 R2"
            self.message("Lowest function level of a DC: (Windows) " + outstr)

        elif subcommand == "raise":
            msgs = []

            if domain_level is not None:
                if domain_level == "2003":
                    new_level_domain = DS_DOMAIN_FUNCTION_2003
                elif domain_level == "2008":
                    new_level_domain = DS_DOMAIN_FUNCTION_2008
                elif domain_level == "2008_R2":
                    new_level_domain = DS_DOMAIN_FUNCTION_2008_R2

                if new_level_domain <= level_domain and level_domain_mixed == 0:
                    raise CommandError(
                        "Domain function level can't be smaller than or equal to the actual one!"
                    )

                if new_level_domain > min_level_dc:
                    raise CommandError(
                        "Domain function level can't be higher than the lowest function level of a DC!"
                    )

                # Deactivate mixed/interim domain support
                if level_domain_mixed != 0:
                    # Directly on the base DN
                    m = ldb.Message()
                    m.dn = ldb.Dn(samdb, domain_dn)
                    m["nTMixedDomain"] = ldb.MessageElement(
                        "0", ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                    samdb.modify(m)
                    # Under partitions
                    m = ldb.Message()
                    m.dn = ldb.Dn(
                        samdb, "CN=" + lp.get("workgroup") +
                        ",CN=Partitions,%s" % ldb.get_config_basedn())
                    m["nTMixedDomain"] = ldb.MessageElement(
                        "0", ldb.FLAG_MOD_REPLACE, "nTMixedDomain")
                    try:
                        samdb.modify(m)
                    except ldb.LdbError, (enum, emsg):
                        if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                            raise

                # Directly on the base DN
                m = ldb.Message()
                m.dn = ldb.Dn(samdb, domain_dn)
                m["msDS-Behavior-Version"] = ldb.MessageElement(
                    str(new_level_domain), ldb.FLAG_MOD_REPLACE,
                    "msDS-Behavior-Version")
                samdb.modify(m)
                # Under partitions
                m = ldb.Message()
                m.dn = ldb.Dn(
                    samdb, "CN=" + lp.get("workgroup") +
                    ",CN=Partitions,%s" % ldb.get_config_basedn())
                m["msDS-Behavior-Version"] = ldb.MessageElement(
                    str(new_level_domain), ldb.FLAG_MOD_REPLACE,
                    "msDS-Behavior-Version")
                try:
                    samdb.modify(m)
                except ldb.LdbError, (enum, emsg):
                    if enum != ldb.ERR_UNWILLING_TO_PERFORM:
                        raise

                level_domain = new_level_domain
                msgs.append("Domain function level changed!")
Exemple #51
0
class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):

    def setUp(self):
        super(AuthLogPassChangeTests, self).setUp()

        self.remoteAddress = os.environ["CLIENT_IP"]
        self.server_ip = os.environ["SERVER_IP"]

        host = "ldap://%s" % os.environ["SERVER"]
        self.ldb = SamDB(url=host,
                         session_info=system_session(),
                         credentials=self.get_credentials(),
                         lp=self.get_loadparm())

        print "ldb %s" % type(self.ldb)
        # Gets back the basedn
        base_dn = self.ldb.domain_dn()
        print "base_dn %s" % base_dn

        # Gets back the configuration basedn
        configuration_dn = self.ldb.get_config_basedn().get_linearized()

        # Get the old "dSHeuristics" if it was set
        dsheuristics = self.ldb.get_dsheuristics()

        # Set the "dSHeuristics" to activate the correct "userPassword"
        # behaviour
        self.ldb.set_dsheuristics("000000001")

        # Reset the "dSHeuristics" as they were before
        self.addCleanup(self.ldb.set_dsheuristics, dsheuristics)

        # Get the old "minPwdAge"
        minPwdAge = self.ldb.get_minPwdAge()

        # Set it temporarily to "0"
        self.ldb.set_minPwdAge("0")
        self.base_dn = self.ldb.domain_dn()

        # Reset the "minPwdAge" as it was before
        self.addCleanup(self.ldb.set_minPwdAge, minPwdAge)

        # (Re)adds the test user USER_NAME with password USER_PASS
        delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
        self.ldb.add({
             "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
             "objectclass": "user",
             "sAMAccountName": USER_NAME,
             "userPassword": USER_PASS
        })

        # discard any auth log messages for the password setup
        self.discardMessages()

    def tearDown(self):
        super(AuthLogPassChangeTests, self).tearDown()


    def test_admin_change_password(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_OK" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template = self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        net.change_password(newpassword=password.encode('utf-8'),
                            username=USER_NAME,
                            oldpassword=USER_PASS)


        messages = self.waitForMessages(isLastExpectedMessage)
        print "Received %d messages" % len(messages)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")

    def test_admin_change_password_new_password_fails_restriction(self):
        def isLastExpectedMessage(msg):
            return (msg["type"] == "Authentication" and
                    msg["Authentication"]["status"]
                        == "NT_STATUS_PASSWORD_RESTRICTION" and
                    msg["Authentication"]["serviceDescription"]
                        == "SAMR Password Change" and
                    msg["Authentication"]["authDescription"]
                        == "samr_ChangePasswordUser3")

        creds = self.insta_creds(template=self.get_credentials())

        lp = self.get_loadparm()
        net = Net(creds, lp, server=self.server_ip)
        password = "******"

        exception_thrown = False
        try:
            net.change_password(newpassword=password.encode('utf-8'),
                                oldpassword=USER_PASS,
                                username=USER_NAME)
        except Exception, msg:
            exception_thrown = True
        self.assertEquals(True, exception_thrown,
                          "Expected exception not thrown")

        messages = self.waitForMessages(isLastExpectedMessage)
        self.assertEquals(8,
                          len(messages),
                          "Did not receive the expected number of messages")