Exemple #1
0
def main():
    lp = LoadParm()
    lp.load_default()

    print("\nLoaded loadparm:")
    print("  samdb_url:      ", lp.samdb_url())
    print("  server_role:    ", lp.server_role())

    creds = Credentials()
    creds.guess(lp)
    print("\nCredentials:")
    creds.set_kerberos_state(MUST_USE_KERBEROS)
    # If MUST_USE_KERBEROS and we have no ticket, yields this error:
    # "Failed to connect to 'ldap://dc1' with backend 'ldap': LDAP client internal error: NT_STATUS_INVALID_PARAMETER"

    # Local
    #samdb = SamDB(lp=lp)

    # Remote
    samdb = SamDB(lp=lp, url='ldap://dc1', credentials=creds)

    print("\nOpened SAM DB:")
    print("  domain_dn:      ", samdb.domain_dn())
    print("  domain_dns_name:", samdb.domain_dns_name())

    for q in sys.argv[1:]:
        query(samdb, q)
Exemple #2
0
    def run(self, sambaopts=None, credopts=None, server=None, targetdir=None,
            no_secrets=False, backend_store=None):
        logger = self.get_logger()
        logger.setLevel(logging.DEBUG)

        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)

        # Make sure we have all the required args.
        if server is None:
            raise CommandError('Server required')

        check_targetdir(logger, targetdir)

        tmpdir = tempfile.mkdtemp(dir=targetdir)

        # Run a clone join on the remote
        include_secrets = not no_secrets
        ctx = join_clone(logger=logger, creds=creds, lp=lp,
                         include_secrets=include_secrets, server=server,
                         dns_backend='SAMBA_INTERNAL', targetdir=tmpdir,
                         backend_store=backend_store)

        # get the paths used for the clone, then drop the old samdb connection
        paths = ctx.paths
        del ctx

        # Get a free RID to use as the new DC's SID (when it gets restored)
        remote_sam = SamDB(url='ldap://' + server, credentials=creds,
                           session_info=system_session(), lp=lp)
        new_sid = get_sid_for_restore(remote_sam, logger)
        realm = remote_sam.domain_dns_name()

        # Grab the remote DC's sysvol files and bundle them into a tar file
        sysvol_tar = os.path.join(tmpdir, 'sysvol.tar.gz')
        smb_conn = smb_sysvol_conn(server, lp, creds)
        backup_online(smb_conn, sysvol_tar, remote_sam.get_domain_sid())

        # remove the default sysvol files created by the clone (we want to
        # make sure we restore the sysvol.tar.gz files instead)
        shutil.rmtree(paths.sysvol)

        # Edit the downloaded sam.ldb to mark it as a backup
        samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp)
        time_str = get_timestamp()
        add_backup_marker(samdb, "backupDate", time_str)
        add_backup_marker(samdb, "sidForRestore", new_sid)
        add_backup_marker(samdb, "backupType", "online")

        # ensure the admin user always has a password set (same as provision)
        if no_secrets:
            set_admin_password(logger, samdb)

        # Add everything in the tmpdir to the backup tar file
        backup_file = backup_filepath(targetdir, realm, time_str)
        create_log_file(tmpdir, lp, "online", server, include_secrets)
        create_backup_tar(logger, tmpdir, backup_file)

        shutil.rmtree(tmpdir)
Exemple #3
0
    def run(self, sambaopts=None, credopts=None, server=None, targetdir=None):
        logger = self.get_logger()
        logger.setLevel(logging.DEBUG)

        # Make sure we have all the required args.
        check_online_backup_args(logger, credopts, server, targetdir)

        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)

        if not os.path.exists(targetdir):
            logger.info('Creating targetdir %s...' % targetdir)
            os.makedirs(targetdir)

        tmpdir = tempfile.mkdtemp(dir=targetdir)

        # Run a clone join on the remote
        ctx = join_clone(logger=logger,
                         creds=creds,
                         lp=lp,
                         include_secrets=True,
                         dns_backend='SAMBA_INTERNAL',
                         server=server,
                         targetdir=tmpdir)

        # get the paths used for the clone, then drop the old samdb connection
        paths = ctx.paths
        del ctx

        # Get a free RID to use as the new DC's SID (when it gets restored)
        remote_sam = SamDB(url='ldap://' + server,
                           credentials=creds,
                           session_info=system_session(),
                           lp=lp)
        new_sid = get_sid_for_restore(remote_sam)
        realm = remote_sam.domain_dns_name()

        # Grab the remote DC's sysvol files and bundle them into a tar file
        sysvol_tar = os.path.join(tmpdir, 'sysvol.tar.gz')
        smb_conn = smb.SMB(server, "sysvol", lp=lp, creds=creds)
        backup_online(smb_conn, sysvol_tar, remote_sam.get_domain_sid())

        # remove the default sysvol files created by the clone (we want to
        # make sure we restore the sysvol.tar.gz files instead)
        shutil.rmtree(paths.sysvol)

        # Edit the downloaded sam.ldb to mark it as a backup
        samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp)
        time_str = get_timestamp()
        add_backup_marker(samdb, "backupDate", time_str)
        add_backup_marker(samdb, "sidForRestore", new_sid)

        # Add everything in the tmpdir to the backup tar file
        backup_file = backup_filepath(targetdir, realm, time_str)
        create_backup_tar(logger, tmpdir, backup_file)

        shutil.rmtree(tmpdir)
Exemple #4
0
def main():
    args = parse_args()
    logging.basicConfig(level=logging.DEBUG if args.debug else logging.WARNING)

    inpipe = sys.stdin
    outpipe = sys.stdout

    if args.unix:
        import socket
        try:
            os.remove(args.unix)
        except OSError:
            pass

        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.bind(args.unix)
        os.chmod(args.unix, 0o777)
        logger.debug("Bound unix socket: {}".format(args.unix))

        logger.info("Waiting for connection at {}".format(args.unix))
        sock.listen(1)
        csock, client_address = sock.accept()

        logger.info("Accepted connection from {}".format(client_address))
        inpipe = outpipe = csock.makefile()

    lp = LoadParm()
    lp.load_default()

    creds = Credentials()
    creds.guess(lp)
    creds.set_kerberos_state(MUST_USE_KERBEROS)
    # If MUST_USE_KERBEROS and we have no ticket, yields this error:
    # "Failed to connect to 'ldap://dc1' with backend 'ldap': LDAP client
    # internal error: NT_STATUS_INVALID_PARAMETER"

    # lp is required by ldap_connect_send() -> lpcfg_resolve_context()
    samdb = SamDB(lp=lp, url=args.url, credentials=creds)

    logger.debug("Opened SAM DB:")
    logger.debug("  domain_dn:       {}".format(samdb.domain_dn()))
    logger.debug("  domain_dns_name: {}".format(samdb.domain_dns_name()))

    try:
        r = SambaResponder(samdb, inpipe, outpipe)
        r.run()
    finally:
        if args.unix:
            try:
                os.remove(args.unix)
            except OSError:
                pass
Exemple #5
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)
Exemple #6
0
class RodcTests(samba.tests.TestCase):
    def setUp(self):
        super(RodcTests, self).setUp()
        self.samdb = SamDB(HOST,
                           credentials=CREDS,
                           session_info=system_session(LP),
                           lp=LP)

        self.base_dn = self.samdb.domain_dn()

        root = self.samdb.search(base='',
                                 scope=ldb.SCOPE_BASE,
                                 attrs=['dsServiceName'])
        self.service = root[0]['dsServiceName'][0]
        self.tag = uuid.uuid4().hex

    def test_add_replicated_objects(self):
        for o in (
            {
                'dn': "ou=%s1,%s" % (self.tag, self.base_dn),
                "objectclass": "organizationalUnit"
            },
            {
                'dn': "cn=%s2,%s" % (self.tag, self.base_dn),
                "objectclass": "user"
            },
            {
                'dn': "cn=%s3,%s" % (self.tag, self.base_dn),
                "objectclass": "group"
            },
            {
                'dn': "cn=%s4,%s" % (self.tag, self.service),
                "objectclass": "NTDSConnection",
                'enabledConnection': 'TRUE',
                'fromServer': self.base_dn,
                'options': '0'
            },
        ):
            try:
                self.samdb.add(o)
                self.fail("Failed to fail to add %s" % o['dn'])
            except ldb.LdbError as e:
                (ecode, emsg) = e.args
                if ecode != ldb.ERR_REFERRAL:
                    print(emsg)
                    self.fail("Adding %s: ldb error: %s %s, wanted referral" %
                              (o['dn'], ecode, emsg))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    try:
                        tmpdb = SamDB(address,
                                      credentials=CREDS,
                                      session_info=system_session(LP),
                                      lp=LP)
                        tmpdb.add(o)
                        tmpdb.delete(o['dn'])
                    except ldb.LdbError as e:
                        self.fail("couldn't modify referred location %s" %
                                  address)

                    if address.lower().startswith(
                            self.samdb.domain_dns_name()):
                        self.fail(
                            "referral address did not give a specific DC")

    def test_modify_replicated_attributes(self):
        # some timestamp ones
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        value = 'hallooo'
        for attr in ['carLicense', 'middleName']:
            msg = ldb.Message()
            msg.dn = ldb.Dn(self.samdb, dn)
            msg[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, attr)
            try:
                self.samdb.modify(msg)
                self.fail("Failed to fail to modify %s %s" % (dn, attr))
            except ldb.LdbError as e1:
                (ecode, emsg) = e1.args
                if ecode != ldb.ERR_REFERRAL:
                    self.fail("Failed to REFER when trying to modify %s %s" %
                              (dn, attr))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    try:
                        tmpdb = SamDB(address,
                                      credentials=CREDS,
                                      session_info=system_session(LP),
                                      lp=LP)
                        tmpdb.modify(msg)
                    except ldb.LdbError as e:
                        self.fail("couldn't modify referred location %s" %
                                  address)

                    if address.lower().startswith(
                            self.samdb.domain_dns_name()):
                        self.fail(
                            "referral address did not give a specific DC")

    def test_modify_nonreplicated_attributes(self):
        # some timestamp ones
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        value = '123456789'
        for attr in ['badPwdCount', 'lastLogon', 'lastLogoff']:
            m = ldb.Message()
            m.dn = ldb.Dn(self.samdb, dn)
            m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, attr)
            # Windows refers these ones even though they are non-replicated
            try:
                self.samdb.modify(m)
                self.fail("Failed to fail to modify %s %s" % (dn, attr))
            except ldb.LdbError as e2:
                (ecode, emsg) = e2.args
                if ecode != ldb.ERR_REFERRAL:
                    self.fail("Failed to REFER when trying to modify %s %s" %
                              (dn, attr))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    if address.lower().startswith(
                            self.samdb.domain_dns_name()):
                        self.fail(
                            "referral address did not give a specific DC")

    def test_modify_nonreplicated_reps_attributes(self):
        # some timestamp ones
        dn = self.base_dn

        m = ldb.Message()
        m.dn = ldb.Dn(self.samdb, dn)
        attr = 'repsFrom'

        res = self.samdb.search(dn, scope=ldb.SCOPE_BASE, attrs=['repsFrom'])
        rep = ndr_unpack(drsblobs.repsFromToBlob,
                         res[0]['repsFrom'][0],
                         allow_remaining=True)
        rep.ctr.result_last_attempt = -1
        value = ndr_pack(rep)

        m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, attr)
        try:
            self.samdb.modify(m)
            self.fail("Failed to fail to modify %s %s" % (dn, attr))
        except ldb.LdbError as e3:
            (ecode, emsg) = e3.args
            if ecode != ldb.ERR_REFERRAL:
                self.fail("Failed to REFER when trying to modify %s %s" %
                          (dn, attr))
            else:
                m = re.search(r'(ldap://[^>]+)>', emsg)
                if m is None:
                    self.fail("referral seems not to refer to anything")
                address = m.group(1)

                if address.lower().startswith(self.samdb.domain_dns_name()):
                    self.fail("referral address did not give a specific DC")

    def test_delete_special_objects(self):
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        try:
            self.samdb.delete(dn)
            self.fail("Failed to fail to delete %s" % (dn))
        except ldb.LdbError as e4:
            (ecode, emsg) = e4.args
            if ecode != ldb.ERR_REFERRAL:
                print(ecode, emsg)
                self.fail("Failed to REFER when trying to delete %s" % dn)
            else:
                m = re.search(r'(ldap://[^>]+)>', emsg)
                if m is None:
                    self.fail("referral seems not to refer to anything")
                address = m.group(1)

                if address.lower().startswith(self.samdb.domain_dns_name()):
                    self.fail("referral address did not give a specific DC")

    def test_no_delete_nonexistent_objects(self):
        dn = 'CN=does-not-exist-%s,CN=Users,%s' % (self.tag, self.base_dn)
        try:
            self.samdb.delete(dn)
            self.fail("Failed to fail to delete %s" % (dn))
        except ldb.LdbError as e5:
            (ecode, emsg) = e5.args
            if ecode != ldb.ERR_NO_SUCH_OBJECT:
                print(ecode, emsg)
                self.fail("Failed to NO_SUCH_OBJECT when trying to delete "
                          "%s (which does not exist)" % dn)
Exemple #7
0
class RodcTests(samba.tests.TestCase):
    def setUp(self):
        super(RodcTests, self).setUp()
        self.samdb = SamDB(HOST,
                           credentials=CREDS,
                           session_info=system_session(LP),
                           lp=LP)

        self.base_dn = self.samdb.domain_dn()

        root = self.samdb.search(base='',
                                 scope=ldb.SCOPE_BASE,
                                 attrs=['dsServiceName'])
        self.service = root[0]['dsServiceName'][0]
        self.tag = uuid.uuid4().hex

    def test_add_replicated_objects(self):
        for o in (
            {
                'dn': "ou=%s1,%s" % (self.tag, self.base_dn),
                "objectclass": "organizationalUnit"
            },
            {
                'dn': "cn=%s2,%s" % (self.tag, self.base_dn),
                "objectclass": "user"
            },
            {
                'dn': "cn=%s3,%s" % (self.tag, self.base_dn),
                "objectclass": "group"
            },
            {
                'dn': "cn=%s4,%s" % (self.tag, self.service),
                "objectclass": "NTDSConnection",
                'enabledConnection': 'TRUE',
                'fromServer': self.base_dn,
                'options': '0'
            },
        ):
            try:
                self.samdb.add(o)
                self.fail("Failed to fail to add %s" % o['dn'])
            except ldb.LdbError as (ecode, emsg):
                if ecode != ldb.ERR_REFERRAL:
                    print emsg
                    self.fail("Adding %s: ldb error: %s %s, wanted referral" %
                              (o['dn'], ecode, emsg))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    try:
                        tmpdb = SamDB(address,
                                      credentials=CREDS,
                                      session_info=system_session(LP),
                                      lp=LP)
                        tmpdb.add(o)
                        tmpdb.delete(o['dn'])
                    except ldb.LdbError, e:
                        self.fail("couldn't modify referred location %s" %
                                  address)

                    if address.lower().startswith(
                            self.samdb.domain_dns_name()):
                        self.fail(
                            "referral address did not give a specific DC")
Exemple #8
0
    def run(self,
            new_domain_name,
            new_dns_realm,
            sambaopts=None,
            credopts=None,
            server=None,
            targetdir=None,
            keep_dns_realm=False):
        logger = self.get_logger()
        logger.setLevel(logging.INFO)

        # Make sure we have all the required args.
        check_online_backup_args(logger, credopts, server, targetdir)
        delete_old_dns = not keep_dns_realm

        new_dns_realm = new_dns_realm.lower()
        new_domain_name = new_domain_name.upper()

        new_base_dn = samba.dn_from_dns_name(new_dns_realm)
        logger.info("New realm for backed up domain: %s" % new_dns_realm)
        logger.info("New base DN for backed up domain: %s" % new_base_dn)
        logger.info("New domain NetBIOS name: %s" % new_domain_name)

        tmpdir = tempfile.mkdtemp(dir=targetdir)

        # Clone and rename the remote server
        lp = sambaopts.get_loadparm()
        creds = credopts.get_credentials(lp)
        ctx = DCCloneAndRenameContext(new_base_dn,
                                      new_domain_name,
                                      new_dns_realm,
                                      logger=logger,
                                      creds=creds,
                                      lp=lp,
                                      include_secrets=True,
                                      dns_backend='SAMBA_INTERNAL',
                                      server=server,
                                      targetdir=tmpdir)
        ctx.do_join()

        # get the paths used for the clone, then drop the old samdb connection
        del ctx.local_samdb
        paths = ctx.paths

        # get a free RID to use as the new DC's SID (when it gets restored)
        remote_sam = SamDB(url='ldap://' + server,
                           credentials=creds,
                           session_info=system_session(),
                           lp=lp)
        new_sid = get_sid_for_restore(remote_sam)
        old_realm = remote_sam.domain_dns_name()

        # Grab the remote DC's sysvol files and bundle them into a tar file.
        # Note we end up with 2 sysvol dirs - the original domain's files (that
        # use the old realm) backed here, as well as default files generated
        # for the new realm as part of the clone/join.
        sysvol_tar = os.path.join(tmpdir, 'sysvol.tar.gz')
        smb_conn = smb.SMB(server, "sysvol", lp=lp, creds=creds)
        backup_online(smb_conn, sysvol_tar, remote_sam.get_domain_sid())

        # connect to the local DB (making sure we use the new/renamed config)
        lp.load(paths.smbconf)
        samdb = SamDB(url=paths.samdb, session_info=system_session(), lp=lp)

        # Edit the cloned sam.ldb to mark it as a backup
        time_str = get_timestamp()
        add_backup_marker(samdb, "backupDate", time_str)
        add_backup_marker(samdb, "sidForRestore", new_sid)
        add_backup_marker(samdb, "backupRename", old_realm)

        # fix up the DNS objects that are using the old dnsRoot value
        self.update_dns_root(logger, samdb, old_realm, delete_old_dns)

        # update the netBIOS name and the Partition object for the domain
        self.rename_domain_partition(logger, samdb, new_domain_name)

        if delete_old_dns:
            self.delete_old_dns_zones(logger, samdb, old_realm)

        logger.info("Fixing DN attributes after rename...")
        self.fix_old_dn_attributes(samdb)

        # Add everything in the tmpdir to the backup tar file
        backup_file = backup_filepath(targetdir, new_dns_realm, time_str)
        create_backup_tar(logger, tmpdir, backup_file)

        shutil.rmtree(tmpdir)
Exemple #9
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 #10
0
class RodcTests(samba.tests.TestCase):

    def setUp(self):
        super(RodcTests, self).setUp()
        self.samdb = SamDB(HOST, credentials=CREDS,
                           session_info=system_session(LP), lp=LP)

        self.base_dn = self.samdb.domain_dn()

        root = self.samdb.search(base='', scope=ldb.SCOPE_BASE,
                                 attrs=['dsServiceName'])
        self.service = root[0]['dsServiceName'][0]
        self.tag = uuid.uuid4().hex

    def test_add_replicated_objects(self):
        for o in (
                {
                    'dn': "ou=%s1,%s" % (self.tag, self.base_dn),
                    "objectclass": "organizationalUnit"
                },
                {
                    'dn': "cn=%s2,%s" % (self.tag, self.base_dn),
                    "objectclass": "user"
                },
                {
                    'dn': "cn=%s3,%s" % (self.tag, self.base_dn),
                    "objectclass": "group"
                },
                {
                    'dn': "cn=%s4,%s" % (self.tag, self.service),
                    "objectclass": "NTDSConnection",
                    'enabledConnection': 'TRUE',
                    'fromServer': self.base_dn,
                    'options': '0'
                },
        ):
            try:
                self.samdb.add(o)
                self.fail("Failed to fail to add %s" % o['dn'])
            except ldb.LdbError as e:
                (ecode, emsg) = e.args
                if ecode != ldb.ERR_REFERRAL:
                    print(emsg)
                    self.fail("Adding %s: ldb error: %s %s, wanted referral" %
                              (o['dn'], ecode, emsg))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    try:
                        tmpdb = SamDB(address, credentials=CREDS,
                                      session_info=system_session(LP), lp=LP)
                        tmpdb.add(o)
                        tmpdb.delete(o['dn'])
                    except ldb.LdbError as e:
                        self.fail("couldn't modify referred location %s" %
                                  address)

                    if address.lower().startswith(self.samdb.domain_dns_name()):
                        self.fail("referral address did not give a specific DC")

    def test_modify_replicated_attributes(self):
        # some timestamp ones
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        value = 'hallooo'
        for attr in ['carLicense', 'middleName']:
            msg = ldb.Message()
            msg.dn = ldb.Dn(self.samdb, dn)
            msg[attr] = ldb.MessageElement(value,
                                           ldb.FLAG_MOD_REPLACE,
                                           attr)
            try:
                self.samdb.modify(msg)
                self.fail("Failed to fail to modify %s %s" % (dn, attr))
            except ldb.LdbError as e1:
                (ecode, emsg) = e1.args
                if ecode != ldb.ERR_REFERRAL:
                    self.fail("Failed to REFER when trying to modify %s %s" %
                              (dn, attr))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    try:
                        tmpdb = SamDB(address, credentials=CREDS,
                                      session_info=system_session(LP), lp=LP)
                        tmpdb.modify(msg)
                    except ldb.LdbError as e:
                        self.fail("couldn't modify referred location %s" %
                                  address)

                    if address.lower().startswith(self.samdb.domain_dns_name()):
                        self.fail("referral address did not give a specific DC")

    def test_modify_nonreplicated_attributes(self):
        # some timestamp ones
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        value = '123456789'
        for attr in ['badPwdCount', 'lastLogon', 'lastLogoff']:
            m = ldb.Message()
            m.dn = ldb.Dn(self.samdb, dn)
            m[attr] = ldb.MessageElement(value,
                                         ldb.FLAG_MOD_REPLACE,
                                         attr)
            # Windows refers these ones even though they are non-replicated
            try:
                self.samdb.modify(m)
                self.fail("Failed to fail to modify %s %s" % (dn, attr))
            except ldb.LdbError as e2:
                (ecode, emsg) = e2.args
                if ecode != ldb.ERR_REFERRAL:
                    self.fail("Failed to REFER when trying to modify %s %s" %
                              (dn, attr))
                else:
                    m = re.search(r'(ldap://[^>]+)>', emsg)
                    if m is None:
                        self.fail("referral seems not to refer to anything")
                    address = m.group(1)

                    if address.lower().startswith(self.samdb.domain_dns_name()):
                        self.fail("referral address did not give a specific DC")

    def test_modify_nonreplicated_reps_attributes(self):
        # some timestamp ones
        dn = self.base_dn

        m = ldb.Message()
        m.dn = ldb.Dn(self.samdb, dn)
        attr = 'repsFrom'

        res = self.samdb.search(dn, scope=ldb.SCOPE_BASE,
                                attrs=['repsFrom'])
        rep = ndr_unpack(drsblobs.repsFromToBlob, res[0]['repsFrom'][0],
                         allow_remaining=True)
        rep.ctr.result_last_attempt = -1
        value = ndr_pack(rep)

        m[attr] = ldb.MessageElement(value,
                                     ldb.FLAG_MOD_REPLACE,
                                     attr)
        try:
            self.samdb.modify(m)
            self.fail("Failed to fail to modify %s %s" % (dn, attr))
        except ldb.LdbError as e3:
            (ecode, emsg) = e3.args
            if ecode != ldb.ERR_REFERRAL:
                self.fail("Failed to REFER when trying to modify %s %s" %
                          (dn, attr))
            else:
                m = re.search(r'(ldap://[^>]+)>', emsg)
                if m is None:
                    self.fail("referral seems not to refer to anything")
                address = m.group(1)

                if address.lower().startswith(self.samdb.domain_dns_name()):
                    self.fail("referral address did not give a specific DC")

    def test_delete_special_objects(self):
        dn = 'CN=Guest,CN=Users,' + self.base_dn
        try:
            self.samdb.delete(dn)
            self.fail("Failed to fail to delete %s" % (dn))
        except ldb.LdbError as e4:
            (ecode, emsg) = e4.args
            if ecode != ldb.ERR_REFERRAL:
                print(ecode, emsg)
                self.fail("Failed to REFER when trying to delete %s" % dn)
            else:
                m = re.search(r'(ldap://[^>]+)>', emsg)
                if m is None:
                    self.fail("referral seems not to refer to anything")
                address = m.group(1)

                if address.lower().startswith(self.samdb.domain_dns_name()):
                    self.fail("referral address did not give a specific DC")

    def test_no_delete_nonexistent_objects(self):
        dn = 'CN=does-not-exist-%s,CN=Users,%s' % (self.tag, self.base_dn)
        try:
            self.samdb.delete(dn)
            self.fail("Failed to fail to delete %s" % (dn))
        except ldb.LdbError as e5:
            (ecode, emsg) = e5.args
            if ecode != ldb.ERR_NO_SUCH_OBJECT:
                print(ecode, emsg)
                self.fail("Failed to NO_SUCH_OBJECT when trying to delete "
                          "%s (which does not exist)" % dn)