def _test_netlogon(self, binding, checkFunction): def isLastExpectedMessage(msg): return ( msg["type"] == "Authorization" and msg["Authorization"]["serviceDescription"] == "DCE/RPC" and msg["Authorization"]["authType"] == "schannel" and msg["Authorization"]["transportProtection"] == "SEAL") if binding: binding = "[schannel,%s]" % binding else: binding = "[schannel]" machine_creds = Credentials() machine_creds.guess(self.get_loadparm()) machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA) machine_creds.set_password(self.machinepass) machine_creds.set_username(self.netbios_name + "$") netlogon_conn = netlogon.netlogon("ncalrpc:%s" % binding, self.get_loadparm(), machine_creds) messages = self.waitForMessages(isLastExpectedMessage, netlogon_conn) checkFunction(messages)
class CredentialsOptionsDouble(CredentialsOptions): """Command line options for specifying credentials of two servers.""" def __init__(self, parser): CredentialsOptions.__init__(self, parser) self.no_pass2 = True self.add_option("--simple-bind-dn2", metavar="DN2", action="callback", callback=self._set_simple_bind_dn2, type=str, help="DN to use for a simple bind") self.add_option("--password2", metavar="PASSWORD2", action="callback", help="Password", type=str, callback=self._set_password2) self.add_option("--username2", metavar="USERNAME2", action="callback", type=str, help="Username for second server", callback=self._parse_username2) self.add_option("--workgroup2", metavar="WORKGROUP2", action="callback", type=str, help="Workgroup for second server", callback=self._parse_workgroup2) self.add_option("--no-pass2", action="store_true", help="Don't ask for a password for the second server") self.add_option("--kerberos2", metavar="KERBEROS2", action="callback", type=str, help="Use Kerberos", callback=self._set_kerberos2) self.creds2 = Credentials() def _parse_username2(self, option, opt_str, arg, parser): self.creds2.parse_string(arg) def _parse_workgroup2(self, option, opt_str, arg, parser): self.creds2.set_domain(arg) def _set_password2(self, option, opt_str, arg, parser): self.creds2.set_password(arg) self.no_pass2 = False def _set_kerberos2(self, option, opt_str, arg, parser): if bool(arg) or arg.lower() == "yes": self.creds2.set_kerberos_state(MUST_USE_KERBEROS) else: self.creds2.set_kerberos_state(DONT_USE_KERBEROS) def _set_simple_bind_dn2(self, option, opt_str, arg, parser): self.creds2.set_bind_dn(arg) def get_credentials2(self, lp, guess=True): """Obtain the credentials set on the command-line. :param lp: Loadparm object to use. :param guess: Try guess Credentials from environment :return: Credentials object """ if guess: self.creds2.guess(lp) elif not self.creds2.get_username(): self.creds2.set_anonymous() if self.no_pass2: self.creds2.set_cmdline_callbacks() return self.creds2
def setUpClass(cls): cls.lp = cls.get_loadparm(cls) cls.username = os.environ["USERNAME"] cls.password = os.environ["PASSWORD"] cls.host = os.environ["SERVER"] c = Credentials() c.set_username(cls.username) c.set_password(cls.password) try: realm = os.environ["REALM"] c.set_realm(realm) except KeyError: pass try: domain = os.environ["DOMAIN"] c.set_domain(domain) except KeyError: pass c.guess() cls.credentials = c cls.session = system_session() cls.ldb = SamDB(url="ldap://%s" % cls.host, session_info=cls.session, credentials=cls.credentials, lp=cls.lp) # fetch the dnsHostName from the RootDse res = cls.ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["dnsHostName"]) cls.dns_host_name = str(res[0]['dnsHostName'])
def user_policy_apply(user, password): user = user.split('\\')[-1] logger = logging.getLogger('gpupdate') logger.addHandler(logging.StreamHandler(sys.stdout)) logger.setLevel(logging.CRITICAL) log_level = lp.log_level() if log_level == 1: logger.setLevel(logging.ERROR) elif log_level == 2: logger.setLevel(logging.WARNING) elif log_level == 3: logger.setLevel(logging.INFO) elif log_level >= 4: logger.setLevel(logging.DEBUG) lp = LoadParm() lp.load_default() creds = Credentials() creds.guess(lp) creds.set_username(user) creds.set_password(password) _, user_exts = get_gp_client_side_extensions(logger) gp_extensions = [] for ext in user_exts: gp_extensions.append(ext(logger, lp, creds, store)) cache_dir = lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) try: apply_gp(lp, creds, logger, store, gp_extensions) except NTSTATUSError as e: logger.info(e.message)
def add_unpriv_user(samdb, ou, username, writeable_objects=None, password="******"): creds = Credentials() creds.set_username(username) creds.set_password(password) creds.set_domain(CREDS.get_domain()) creds.set_realm(CREDS.get_realm()) creds.set_workstation(CREDS.get_workstation()) creds.set_gensec_features(CREDS.get_gensec_features() | FEATURE_SEAL) creds.set_kerberos_state(DONT_USE_KERBEROS) dnstr = f"CN={username},{ou}" # like, WTF, samdb.newuser(), this is what you make us do. short_ou = ou.split(',', 1)[0] samdb.newuser(username, password, userou=short_ou) if writeable_objects: sd_utils = SDUtils(samdb) sid = sd_utils.get_object_sid(dnstr) for obj in writeable_objects: mod = f"(OA;CI;WP;{ SPN_GUID };;{ sid })" sd_utils.dacl_add_ace(obj, mod) unpriv_samdb = get_samdb(creds=creds) return unpriv_samdb
def string_to_key(cls, string, salt, params): utf8string = get_string(string) tmp = Credentials() tmp.set_anonymous() tmp.set_password(utf8string) nthash = tmp.get_nt_hash() return Key(cls.enctype, nthash)
class KCCTests(samba.tests.TestCase): def setUp(self): super(KCCTests, self).setUp() self.lp = LoadParm() self.creds = Credentials() self.creds.guess(self.lp) self.creds.set_username(os.environ["USERNAME"]) self.creds.set_password(os.environ["PASSWORD"]) def test_list_dsas(self): my_kcc = kcc.KCC(unix_now, False, False, False, False) my_kcc.load_samdb("ldap://%s" % os.environ["SERVER"], self.lp, self.creds) dsas = my_kcc.list_dsas() env = os.environ['TEST_ENV'] for expected_dsa in ENV_DSAS[env]: self.assertIn(expected_dsa, dsas) def test_verify(self): """check that the KCC generates graphs that pass its own verify option. This is not a spectacular achievement when there are only a couple of nodes to connect, but it shows something. """ my_kcc = kcc.KCC(unix_now, readonly=True, verify=True, debug=False, dot_file_dir=None) my_kcc.run("ldap://%s" % os.environ["SERVER"], self.lp, self.creds, attempt_live_connections=False)
class KCCTests(samba.tests.TestCase): def setUp(self): super(KCCTests, self).setUp() self.lp = LoadParm() self.creds = Credentials() self.creds.guess(self.lp) self.creds.set_username(os.environ["USERNAME"]) self.creds.set_password(os.environ["PASSWORD"]) def test_list_dsas(self): my_kcc = kcc.KCC(unix_now, False, False, False, False) my_kcc.load_samdb("ldap://%s" % os.environ["SERVER"], self.lp, self.creds) dsas = my_kcc.list_dsas() env = os.environ['TEST_ENV'] for expected_dsa in ENV_DSAS[env]: self.assertIn(expected_dsa, dsas) def test_verify(self): """check that the KCC generates graphs that pass its own verify option. This is not a spectacular achievement when there are only a couple of nodes to connect, but it shows something. """ my_kcc = kcc.KCC(unix_now, readonly=True, verify=True, debug=False, dot_file_dir=None) my_kcc.run("ldap://%s" % os.environ["SERVER"], self.lp, self.creds, attempt_live_connections=False)
def _test_netlogon(self, name, pwd, status, checkFunction): def isLastExpectedMessage(msg): return ( msg["type"] == "Authentication" and msg["Authentication"]["serviceDescription"] == "NETLOGON" and msg["Authentication"]["authDescription"] == "ServerAuthenticate" and msg["Authentication"]["status"] == status) machine_creds = Credentials() machine_creds.guess(self.get_loadparm()) machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA) machine_creds.set_password(pwd) machine_creds.set_username(name + "$") try: netlogon.netlogon("ncalrpc:[schannel]", self.get_loadparm(), machine_creds) self.fail("NTSTATUSError not raised") except NTSTATUSError: pass messages = self.waitForMessages(isLastExpectedMessage) checkFunction(messages)
def insta_creds(self, template=None, username=None, userpass=None, kerberos_state=None): if template is None: assert template is not None if username is not None: assert userpass is not None if username is None: assert userpass is None username = template.get_username() userpass = template.get_password() if kerberos_state is None: kerberos_state = template.get_kerberos_state() # get a copy of the global creds or a the passed in creds c = Credentials() c.set_username(username) c.set_password(userpass) c.set_domain(template.get_domain()) c.set_realm(template.get_realm()) c.set_workstation(template.get_workstation()) c.set_gensec_features(c.get_gensec_features() | gensec.FEATURE_SEAL) c.set_kerberos_state(kerberos_state) return c
def insta_creds(self, template=None, username=None, userpass=None, kerberos_state=None): if template is None: assert template is not None if username is not None: assert userpass is not None if username is None: assert userpass is None username = template.get_username() userpass = template.get_password() if kerberos_state is None: kerberos_state = template.get_kerberos_state() # get a copy of the global creds or a the passed in creds c = Credentials() c.set_username(username) c.set_password(userpass) c.set_domain(template.get_domain()) c.set_realm(template.get_realm()) c.set_workstation(template.get_workstation()) c.set_gensec_features(c.get_gensec_features() | gensec.FEATURE_SEAL) c.set_kerberos_state(kerberos_state) return c
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 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 setUp(self): super(DrsRodcTestCase, self).setUp() self.base_dn = self.ldb_dc1.get_default_basedn() rand = random.randint(1, 10000000) self.ou = "OU=test_drs_rodc%s,%s" % (rand, self.base_dn) self.ldb_dc1.add({ "dn": self.ou, "objectclass": "organizationalUnit" }) self.allowed_group = "CN=Allowed RODC Password Replication Group,CN=Users,%s" % self.base_dn self.site = self.ldb_dc1.server_site_name() self.rodc_name = "TESTRODCDRS%s" % rand self.rodc_pass = "******" self.computer_dn = "CN=%s,OU=Domain Controllers,%s" % (self.rodc_name, self.base_dn) self.rodc_ctx = dc_join(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(), site=self.site, netbios_name=self.rodc_name, targetdir=None, domain=None, machinepass=self.rodc_pass) self._create_rodc(self.rodc_ctx) self.rodc_ctx.create_tmp_samdb() self.tmp_samdb = self.rodc_ctx.tmp_samdb rodc_creds = Credentials() rodc_creds.guess(self.rodc_ctx.lp) rodc_creds.set_username(self.rodc_name+'$') rodc_creds.set_password(self.rodc_pass) self.rodc_creds = rodc_creds (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, rodc_creds)
def setUp(self): super(DrsRodcTestCase, self).setUp() self.base_dn = self.ldb_dc1.get_default_basedn() self.ou = samba.tests.create_test_ou(self.ldb_dc1, "test_drs_rodc") self.allowed_group = "CN=Allowed RODC Password Replication Group,CN=Users,%s" % self.base_dn self.site = self.ldb_dc1.server_site_name() self.rodc_name = "TESTRODCDRS%s" % random.randint(1, 10000000) self.rodc_pass = "******" self.computer_dn = "CN=%s,OU=Domain Controllers,%s" % (self.rodc_name, self.base_dn) self.rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(), site=self.site, netbios_name=self.rodc_name, targetdir=None, domain=None, machinepass=self.rodc_pass) self._create_rodc(self.rodc_ctx) self.rodc_ctx.create_tmp_samdb() self.tmp_samdb = self.rodc_ctx.tmp_samdb rodc_creds = Credentials() rodc_creds.guess(self.rodc_ctx.lp) rodc_creds.set_username(self.rodc_name + '$') rodc_creds.set_password(self.rodc_pass) self.rodc_creds = rodc_creds (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, rodc_creds)
def get_ad_binddn_from_name(base, server, username, password): lp = LoadParm() creds = Credentials() creds.guess(lp) creds.set_username(username) creds.set_password(password) binddn = 'cn=%s,cn=users,%s' % (ldap.dn.escape_dn_chars(username), base) try: samdb = SamDB(url='ldap://%s' % server, session_info=system_session(), credentials=creds, lp=lp) res = samdb.search(base, scope=ldb.SCOPE_SUBTREE, expression=ldap.filter.filter_format( '(samAccountName=%s)', [ username, ]), attrs=['samaccountname']) if res.count == 1: binddn = res.msgs[0].get('dn', idx=0).extended_str() except ldb.LdbError as ex: MODULE.warn('get_dn_from_name() could not get binddn for user %s: %s' % (username, ex)) return binddn
def setUpClass(cls): cls.lp = cls.get_loadparm(cls) cls.username = os.environ["USERNAME"] cls.password = os.environ["PASSWORD"] cls.host = os.environ["SERVER"] c = Credentials() c.set_username(cls.username) c.set_password(cls.password) try: realm = os.environ["REALM"] c.set_realm(realm) except KeyError: pass try: domain = os.environ["DOMAIN"] c.set_domain(domain) except KeyError: pass c.guess() cls.credentials = c cls.session = system_session() cls.ldb = SamDB(url="ldap://%s" % cls.host, session_info=cls.session, credentials=cls.credentials, lp=cls.lp) cls.create_machine_account() cls.create_user_account()
def insta_creds(self, template=None, username=None, userpass=None, kerberos_state=None): if template is None: raise ValueError("you need to supply a Credentials template") if username is not None and userpass is None: raise ValueError( "you cannot set creds username without setting a password") if username is None: assert userpass is None username = template.get_username() userpass = template.get_password() if kerberos_state is None: kerberos_state = template.get_kerberos_state() # get a copy of the global creds or a the passed in creds c = Credentials() c.set_username(username) c.set_password(userpass) c.set_domain(template.get_domain()) c.set_realm(template.get_realm()) c.set_workstation(template.get_workstation()) c.set_gensec_features(c.get_gensec_features() | gensec.FEATURE_SEAL) c.set_kerberos_state(kerberos_state) return c
def join_replicate(ctx): '''replicate the SAM''' print "Starting replication" ctx.local_samdb.transaction_start() try: source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id()) destination_dsa_guid = ctx.ntds_guid if ctx.RODC: repl_creds = Credentials() repl_creds.guess(ctx.lp) repl_creds.set_kerberos_state(DONT_USE_KERBEROS) repl_creds.set_username(ctx.samname) repl_creds.set_password(ctx.acct_pass) else: repl_creds = ctx.creds binding_options = "seal" if int(ctx.lp.get("log level")) >= 5: binding_options += ",print" repl = drs_utils.drs_Replicate( "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options), ctx.lp, repl_creds, ctx.local_samdb) repl.replicate(ctx.schema_dn, source_dsa_invocation_id, destination_dsa_guid, schema=True, rodc=ctx.RODC, replica_flags=ctx.replica_flags) repl.replicate(ctx.config_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) repl.replicate(ctx.base_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) if ctx.RODC: repl.replicate(ctx.acct_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) print "Committing SAM database" except: ctx.local_samdb.transaction_cancel() raise else: ctx.local_samdb.transaction_commit()
def get_user_creds(self): c = Credentials() c.guess() username = samba.tests.env_get_var_value('USERNAME') password = samba.tests.env_get_var_value('PASSWORD') c.set_username(username) c.set_password(password) return c
class CredentialsOptionsDouble(CredentialsOptions): """Command line options for specifying credentials of two servers.""" def __init__(self, parser): CredentialsOptions.__init__(self, parser) self.no_pass2 = True self.add_option("--simple-bind-dn2", metavar="DN2", action="callback", callback=self._set_simple_bind_dn2, type=str, help="DN to use for a simple bind") self.add_option("--password2", metavar="PASSWORD2", action="callback", help="Password", type=str, callback=self._set_password2) self.add_option("--username2", metavar="USERNAME2", action="callback", type=str, help="Username for second server", callback=self._parse_username2) self.add_option("--workgroup2", metavar="WORKGROUP2", action="callback", type=str, help="Workgroup for second server", callback=self._parse_workgroup2) self.add_option("--no-pass2", action="store_true", help="Don't ask for a password for the second server") self.add_option("--kerberos2", metavar="KERBEROS2", action="callback", type=str, help="Use Kerberos", callback=self._set_kerberos2) self.creds2 = Credentials() def _parse_username2(self, option, opt_str, arg, parser): self.creds2.parse_string(arg) def _parse_workgroup2(self, option, opt_str, arg, parser): self.creds2.set_domain(arg) def _set_password2(self, option, opt_str, arg, parser): self.creds2.set_password(arg) self.no_pass2 = False def _set_kerberos2(self, option, opt_str, arg, parser): self.creds2.set_kerberos_state(parse_kerberos_arg(arg, opt_str)) def _set_simple_bind_dn2(self, option, opt_str, arg, parser): self.creds2.set_bind_dn(arg) def get_credentials2(self, lp, guess=True): """Obtain the credentials set on the command-line. :param lp: Loadparm object to use. :param guess: Try guess Credentials from environment :return: Credentials object """ if guess: self.creds2.guess(lp) elif not self.creds2.get_username(): self.creds2.set_anonymous() if self.no_pass2: self.creds2.set_cmdline_callbacks() return self.creds2
def get_creds(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) return creds_tmp
def join_replicate(ctx): '''replicate the SAM''' print "Starting replication" ctx.local_samdb.transaction_start() try: source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id()) if ctx.ntds_guid is None: print("Using DS_BIND_GUID_W2K3") destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3) else: destination_dsa_guid = ctx.ntds_guid if ctx.RODC: repl_creds = Credentials() repl_creds.guess(ctx.lp) repl_creds.set_kerberos_state(DONT_USE_KERBEROS) repl_creds.set_username(ctx.samname) repl_creds.set_password(ctx.acct_pass) else: repl_creds = ctx.creds binding_options = "seal" if int(ctx.lp.get("log level")) >= 5: binding_options += ",print" repl = drs_utils.drs_Replicate( "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options), ctx.lp, repl_creds, ctx.local_samdb) repl.replicate(ctx.schema_dn, source_dsa_invocation_id, destination_dsa_guid, schema=True, rodc=ctx.RODC, replica_flags=ctx.replica_flags) repl.replicate(ctx.config_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) if not ctx.subdomain: repl.replicate(ctx.base_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.domain_replica_flags) if ctx.RODC: repl.replicate(ctx.acct_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) ctx.repl = repl ctx.source_dsa_invocation_id = source_dsa_invocation_id ctx.destination_dsa_guid = destination_dsa_guid print "Committing SAM database" except: ctx.local_samdb.transaction_cancel() raise else: ctx.local_samdb.transaction_commit()
def conectar(self): lp = param.LoadParm() badge = Credentials() badge.guess(lp) badge.set_username(self.username) badge.set_password(self.password) cx = SamDB(url='ldap://localhost', lp=lp, credentials=badge) self.domain = cx.domain_dn() return cx
def get_creds(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) return creds_tmp
class NtlmDisabledTests(TestCase): def setUp(self): super(NtlmDisabledTests, self).setUp() self.lp = self.get_loadparm() self.server = os.getenv("SERVER") self.creds = Credentials() self.creds.guess(self.lp) self.creds.set_username(os.getenv("USERNAME")) self.creds.set_domain(self.server) self.creds.set_password(os.getenv("PASSWORD")) self.creds.set_kerberos_state(DONT_USE_KERBEROS) def tearDown(self): super(NtlmDisabledTests, self).tearDown() def test_ntlm_connection(self): try: conn = srvsvc.srvsvc("ncacn_np:%s[smb2,ntlm]" % self.server, self.lp, self.creds) self.assertIsNotNone(conn) except NTSTATUSError as e: # NTLM might be blocked on this server enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_NTLM_BLOCKED: self.fail("NTLM is disabled on this server") else: raise def test_samr_change_password(self): self.creds.set_kerberos_state(MUST_USE_KERBEROS) conn = samr.samr("ncacn_np:%s[krb5,seal,smb2]" % os.getenv("SERVER")) # we want to check whether this gets rejected outright because NTLM is # disabled, so we don't actually need to encrypt a valid password here server = lsa.String() server.string = self.server username = lsa.String() username.string = os.getenv("USERNAME") try: conn.ChangePasswordUser2(server, username, None, None, True, None, None) except NTSTATUSError as e: # changing passwords should be rejected when NTLM is disabled enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_NTLM_BLOCKED: self.fail("NTLM is disabled on this server") elif enum == ntstatus.NT_STATUS_WRONG_PASSWORD: # expected error case when NTLM is enabled pass else: raise
def connection(self): lp = LoadParm() creds = Credentials() creds.guess(lp) creds.set_username("") creds.set_password("") con = SamDB(url='ldap://192.168.100.26:389', session_info=system_session(), credentials=creds, lp=lp) return con
def get_creds(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 return creds_tmp
def get_creds(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 return creds_tmp
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) ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) return ldb_target
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) ldb_target = SamDB(url=host, credentials=creds_tmp, lp=lp) return ldb_target
class NtlmDisabledTests(TestCase): def setUp(self): super(NtlmDisabledTests, self).setUp() self.lp = self.get_loadparm() self.server = os.getenv("SERVER") self.creds = Credentials() self.creds.guess(self.lp) self.creds.set_username(os.getenv("USERNAME")) self.creds.set_domain(self.server) self.creds.set_password(os.getenv("PASSWORD")) self.creds.set_kerberos_state(DONT_USE_KERBEROS) def tearDown(self): super(NtlmDisabledTests, self).tearDown() def test_ntlm_connection(self): try: conn = srvsvc.srvsvc("ncacn_np:%s[smb2,ntlm]" % self.server, self.lp, self.creds) self.assertIsNotNone(conn) except NTSTATUSError as e: # NTLM might be blocked on this server enum = ctypes.c_uint32(e[0]).value if enum == ntstatus.NT_STATUS_NTLM_BLOCKED: self.fail("NTLM is disabled on this server") else: raise def test_samr_change_password(self): self.creds.set_kerberos_state(MUST_USE_KERBEROS) conn = samr.samr("ncacn_np:%s[krb5,seal,smb2]" % os.getenv("SERVER")) # we want to check whether this gets rejected outright because NTLM is # disabled, so we don't actually need to encrypt a valid password here server = lsa.String() server.string = self.server username = lsa.String() username.string = os.getenv("USERNAME") try: conn.ChangePasswordUser2(server, username, None, None, True, None, None) except NTSTATUSError as e: # changing passwords should be rejected when NTLM is disabled enum = ctypes.c_uint32(e[0]).value if enum == ntstatus.NT_STATUS_NTLM_BLOCKED: self.fail("NTLM is disabled on this server") elif enum == ntstatus.NT_STATUS_WRONG_PASSWORD: # expected error case when NTLM is enabled pass else: raise
def get_ldb_connection(self, username, password, ldaphost): """Returns an LDB connection using the specified user's credentials""" creds = self.get_credentials() creds_tmp = Credentials() creds_tmp.set_username(username) creds_tmp.set_password(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) return samba.tests.connect_samdb(ldaphost, credentials=creds_tmp)
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()) features = creds_tmp.get_gensec_features() | gensec.FEATURE_SEAL creds_tmp.set_gensec_features(features) creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) return ldb_target
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
def get_user_creds(self): c = Credentials() c.guess() domain = samba.tests.env_get_var_value('DOMAIN') realm = samba.tests.env_get_var_value('REALM') username = samba.tests.env_get_var_value('USERNAME') password = samba.tests.env_get_var_value('PASSWORD') c.set_domain(domain) c.set_realm(realm) c.set_username(username) c.set_password(password) return c
def get_ldb_connection(self, username, password, ldaphost): """Returns an LDB connection using the specified user's credentials""" creds = self.get_credentials() creds_tmp = Credentials() creds_tmp.set_username(username) creds_tmp.set_password(password) creds_tmp.set_domain(creds.get_domain()) creds_tmp.set_realm(creds.get_realm()) creds_tmp.set_workstation(creds.get_workstation()) features = creds_tmp.get_gensec_features() | gensec.FEATURE_SEAL creds_tmp.set_gensec_features(features) return samba.tests.connect_samdb(ldaphost, credentials=creds_tmp)
def test_msDSRevealedUsers_using_other_RODC(self): """ Ensure that the machine account is tied to the destination DSA. """ # Create a new identical RODC with just the first letter missing other_rodc_name = self.rodc_name[1:] other_rodc_ctx = dc_join(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(), site=self.site, netbios_name=other_rodc_name, targetdir=None, domain=None, machinepass=self.rodc_pass) self._create_rodc(other_rodc_ctx) other_rodc_creds = Credentials() other_rodc_creds.guess(other_rodc_ctx.lp) other_rodc_creds.set_username(other_rodc_name+'$') other_rodc_creds.set_password(self.rodc_pass) (other_rodc_drs, other_rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, other_rodc_creds) rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] user_name = "test_rodcF_%s" % rand user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", [user_name], add_members_operation=True) req10 = self._getnc_req10(dest_dsa=str(other_rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as (enum, estr): self.assertEquals(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED
def get_service_creds(self, allow_missing_password=False): c = Credentials() c.guess() domain = samba.tests.env_get_var_value('DOMAIN') realm = samba.tests.env_get_var_value('REALM') username = samba.tests.env_get_var_value('SERVICE_USERNAME') password = samba.tests.env_get_var_value( 'SERVICE_PASSWORD', allow_missing=allow_missing_password) c.set_domain(domain) c.set_realm(realm) c.set_username(username) if password is not None: c.set_password(password) return c
def create_account(self, name, machine_account=False, spn=None, upn=None): '''Create an account for testing. The dn of the created account is added to self.accounts, which is used by tearDown to clean up the created accounts. ''' dn = "cn=%s,%s" % (name, self.ldb.domain_dn()) # remove the account if it exists, this will happen if a previous test # run failed delete_force(self.ldb, dn) if machine_account: object_class = "computer" account_name = "%s$" % name account_control = str(UF_WORKSTATION_TRUST_ACCOUNT) else: object_class = "user" account_name = name account_control = str(UF_NORMAL_ACCOUNT) password = generate_random_password(32, 32) utf16pw = ('"%s"' % password).encode('utf-16-le') details = { "dn": dn, "objectclass": object_class, "sAMAccountName": account_name, "userAccountControl": account_control, "unicodePwd": utf16pw } if spn is not None: details["servicePrincipalName"] = spn if upn is not None: details["userPrincipalName"] = upn self.ldb.add(details) creds = Credentials() creds.guess(self.lp) creds.set_realm(self.ldb.domain_dns_name().upper()) creds.set_domain(self.ldb.domain_netbios_name().upper()) creds.set_password(password) creds.set_username(account_name) if machine_account: creds.set_workstation(name) # # Save the account name so it can be deleted in the tearDown self.accounts.append(dn) return (creds, dn)
def credenciales(username, password, parametros): """ Más que nada, encapsulo un par de líneas sobre el trabajo con Credentials() Rompe un poco la idea de inyección, pero así las cosas """ cred = Credentials() dominio = parametros.get('workgroup') cred.set_username(username) cred.set_password(password) cred.set_domain(dominio) # TODO: ¿Este tiene algún efecto? cred.set_workstation("") return cred
def get_user_and_ldb(self, username, password, hostname=ldaphost): """Get a connection for a temporarily user that will vanish as soon as the test is over.""" user = self.ldb.newuser(username, password) creds_tmp = Credentials() creds_tmp.set_username(username) creds_tmp.set_password(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) ldb_target = SamDB(url=hostname, credentials=creds_tmp, lp=lp) self.addCleanup(delete_force, self.ldb, self.get_user_dn(username)) return (user, ldb_target)
def authenticate(self, usuario, password): # Connect to samba and attempt to authenticate this user print("SambaServer.authenticate: authenticating with %s and %s" % (usuario, password)) lp = param.LoadParm() badge = Credentials() badge.guess(lp) badge.set_username(usuario) badge.set_password(password) # Intento la conexion. cx = SamDB(url='ldap://localhost', lp=lp, credentials=badge) print("cx returned: %s" % cx) # Get the domain from the SamDB and store it self.domain = cx.domain_dn() # Listo, la gracia es que si logre autenticarme con estos datos, y hay algun # resultado de mis acciones, voy a devolver algo. return cx
def netlogon(self): server = os.environ["SERVER"] host = os.environ["SERVER_IP"] lp = self.get_loadparm() credentials = self.get_credentials() session = system_session() ldb = SamDB(url="ldap://%s" % host, session_info=session, credentials=credentials, lp=lp) machine_pass = samba.generate_random_password(32, 32) machine_name = MACHINE_NAME machine_dn = "cn=%s,%s" % (machine_name, ldb.domain_dn()) delete_force(ldb, machine_dn) utf16pw = ('"%s"' % get_string(machine_pass)).encode('utf-16-le') ldb.add({ "dn": machine_dn, "objectclass": "computer", "sAMAccountName": "%s$" % machine_name, "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), "unicodePwd": utf16pw }) machine_creds = Credentials() machine_creds.guess(lp) machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA) machine_creds.set_kerberos_state(DONT_USE_KERBEROS) machine_creds.set_password(machine_pass) machine_creds.set_username(machine_name + "$") machine_creds.set_workstation(machine_name) netlogon.netlogon("ncacn_ip_tcp:%s[schannel,seal]" % server, lp, machine_creds) delete_force(ldb, machine_dn)
def get_dns_zones(): request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY server = '127.0.0.1' binding_str = 'ncacn_ip_tcp:%s[sign]' % server cred_data = open('/vapour/dnsquery').read().split(':') creds = Credentials() creds.guess(lp) creds.set_username(cred_data[0]) creds.set_password(cred_data[1].rstrip()) dns_conn = dnsserver.dnsserver(binding_str, lp, creds) client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN typeid, res = dns_conn.DnssrvComplexOperation2( client_version, 0, server, None, 'EnumZones', dnsserver.DNSSRV_TYPEID_DWORD, request_filter) return dict(res)
def getUsers(self): lp = param.LoadParm() badge = Credentials() badge.guess(lp) badge.set_username('Administrator') badge.set_password('pa$$w0rd!') print("Getting users") # Binding... cx = SamDB(url='ldap://localhost', lp=lp, credentials=badge) # Search... search_result = cx.search('DC=kajohansen,DC=com', scope=2, expression='(objectClass=user)', attrs=["samaccountname"]) users = [] # list to hold our users # Results... for username in search_result: # print("User: %s" % username.get("samaccountname", idx=0)) users.append(username.get("samaccountname", idx=0)) return users
class PyKrb5CredentialsTests(TestCase): def setUp(self): super(PyKrb5CredentialsTests, self).setUp() self.server = os.environ["SERVER"] self.domain = os.environ["DOMAIN"] self.host = os.environ["SERVER_IP"] self.lp = self.get_loadparm() self.credentials = self.get_credentials() self.session = system_session() self.ldb = SamDB(url="ldap://%s" % self.host, session_info=self.session, credentials=self.credentials, lp=self.lp) self.create_machine_account() def tearDown(self): super(PyKrb5CredentialsTests, self).tearDown() delete_force(self.ldb, self.machine_dn) def test_get_named_ccache(self): name = "MEMORY:py_creds_machine" ccache = self.machine_creds.get_named_ccache(self.lp, name) self.assertEqual(ccache.get_name(), name) def test_get_unnamed_ccache(self): ccache = self.machine_creds.get_named_ccache(self.lp) self.assertIsNotNone(ccache.get_name()) def test_set_named_ccache(self): ccache = self.machine_creds.get_named_ccache(self.lp) creds = Credentials() creds.set_named_ccache(ccache.get_name()) ccache2 = creds.get_named_ccache(self.lp) self.assertEqual(ccache.get_name(), ccache2.get_name()) # # Create the machine account def create_machine_account(self): self.machine_pass = samba.generate_random_password(32, 32) self.machine_name = MACHINE_NAME self.machine_dn = "cn=%s,%s" % (self.machine_name, self.ldb.domain_dn()) # remove the account if it exists, this will happen if a previous test # run failed delete_force(self.ldb, self.machine_dn) # get unicode str for both py2 and py3 pass_unicode = self.machine_pass.encode('utf-8').decode('utf-8') utf16pw = u'"{}"'.format(pass_unicode).encode('utf-16-le') self.ldb.add({ "dn": self.machine_dn, "objectclass": "computer", "sAMAccountName": "%s$" % self.machine_name, "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), "unicodePwd": utf16pw}) self.machine_creds = Credentials() self.machine_creds.guess(self.get_loadparm()) self.machine_creds.set_password(self.machine_pass) self.machine_creds.set_username(self.machine_name + "$") self.machine_creds.set_workstation(self.machine_name)
class PyCredentialsTests(TestCase): def setUp(self): super(PyCredentialsTests, self).setUp() self.server = os.environ["SERVER"] self.domain = os.environ["DOMAIN"] self.host = os.environ["SERVER_IP"] self.lp = self.get_loadparm() self.credentials = self.get_credentials() self.session = system_session() self.ldb = SamDB(url="ldap://%s" % self.host, session_info=self.session, credentials=self.credentials, lp=self.lp) self.create_machine_account() self.create_user_account() def tearDown(self): super(PyCredentialsTests, self).tearDown() delete_force(self.ldb, self.machine_dn) delete_force(self.ldb, self.user_dn) # Until a successful netlogon connection has been established there will # not be a valid authenticator associated with the credentials # and new_client_authenticator should throw a ValueError def test_no_netlogon_connection(self): self.assertRaises(ValueError, self.machine_creds.new_client_authenticator) # Once a netlogon connection has been established, # new_client_authenticator should return a value # def test_have_netlogon_connection(self): c = self.get_netlogon_connection() a = self.machine_creds.new_client_authenticator() self.assertIsNotNone(a) # Get an authenticator and use it on a sequence of operations requiring # an authenticator def test_client_authenticator(self): c = self.get_netlogon_connection() (authenticator, subsequent) = self.get_authenticator(c) self.do_NetrLogonSamLogonWithFlags(c, authenticator, subsequent) (authenticator, subsequent) = self.get_authenticator(c) self.do_NetrLogonGetDomainInfo(c, authenticator, subsequent) (authenticator, subsequent) = self.get_authenticator(c) self.do_NetrLogonGetDomainInfo(c, authenticator, subsequent) (authenticator, subsequent) = self.get_authenticator(c) self.do_NetrLogonGetDomainInfo(c, authenticator, subsequent) def test_SamLogonEx(self): c = self.get_netlogon_connection() logon = samlogon_logon_info(self.domain, self.machine_name, self.user_creds) logon_level = netlogon.NetlogonNetworkTransitiveInformation validation_level = netlogon.NetlogonValidationSamInfo4 netr_flags = 0 try: c.netr_LogonSamLogonEx(self.server, self.user_creds.get_workstation(), logon_level, logon, validation_level, netr_flags) except NTSTATUSError as e: enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_WRONG_PASSWORD: self.fail("got wrong password error") else: raise def test_SamLogonEx_no_domain(self): c = self.get_netlogon_connection() self.user_creds.set_domain('') logon = samlogon_logon_info(self.domain, self.machine_name, self.user_creds) logon_level = netlogon.NetlogonNetworkTransitiveInformation validation_level = netlogon.NetlogonValidationSamInfo4 netr_flags = 0 try: c.netr_LogonSamLogonEx(self.server, self.user_creds.get_workstation(), logon_level, logon, validation_level, netr_flags) except NTSTATUSError as e: enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_WRONG_PASSWORD: self.fail("got wrong password error") else: self.fail("got unexpected error" + str(e)) def test_SamLogonExNTLM(self): c = self.get_netlogon_connection() logon = samlogon_logon_info(self.domain, self.machine_name, self.user_creds, flags=CLI_CRED_NTLM_AUTH) logon_level = netlogon.NetlogonNetworkTransitiveInformation validation_level = netlogon.NetlogonValidationSamInfo4 netr_flags = 0 try: c.netr_LogonSamLogonEx(self.server, self.user_creds.get_workstation(), logon_level, logon, validation_level, netr_flags) except NTSTATUSError as e: enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_WRONG_PASSWORD: self.fail("got wrong password error") else: raise def test_SamLogonExMSCHAPv2(self): c = self.get_netlogon_connection() logon = samlogon_logon_info(self.domain, self.machine_name, self.user_creds, flags=CLI_CRED_NTLM_AUTH) logon.identity_info.parameter_control = MSV1_0_ALLOW_MSVCHAPV2 logon_level = netlogon.NetlogonNetworkTransitiveInformation validation_level = netlogon.NetlogonValidationSamInfo4 netr_flags = 0 try: c.netr_LogonSamLogonEx(self.server, self.user_creds.get_workstation(), logon_level, logon, validation_level, netr_flags) except NTSTATUSError as e: enum = ctypes.c_uint32(e.args[0]).value if enum == ntstatus.NT_STATUS_WRONG_PASSWORD: self.fail("got wrong password error") else: raise # Test Credentials.encrypt_netr_crypt_password # By performing a NetrServerPasswordSet2 # And the logging on using the new password. def test_encrypt_netr_password(self): # Change the password self.do_Netr_ServerPasswordSet2() # Now use the new password to perform an operation srvsvc.srvsvc("ncacn_np:%s" % (self.server), self.lp, self.machine_creds) # Change the current machine account password with a # netr_ServerPasswordSet2 call. def do_Netr_ServerPasswordSet2(self): c = self.get_netlogon_connection() (authenticator, subsequent) = self.get_authenticator(c) PWD_LEN = 32 DATA_LEN = 512 newpass = samba.generate_random_password(PWD_LEN, PWD_LEN) encoded = newpass.encode('utf-16-le') pwd_len = len(encoded) filler = [ord(x) for x in os.urandom(DATA_LEN-pwd_len)] pwd = netlogon.netr_CryptPassword() pwd.length = pwd_len pwd.data = filler + [ord(x) for x in encoded] self.machine_creds.encrypt_netr_crypt_password(pwd) c.netr_ServerPasswordSet2(self.server, self.machine_creds.get_workstation(), SEC_CHAN_WKSTA, self.machine_name, authenticator, pwd) self.machine_pass = newpass self.machine_creds.set_password(newpass) # Establish sealed schannel netlogon connection over TCP/IP # def get_netlogon_connection(self): return netlogon.netlogon("ncacn_ip_tcp:%s[schannel,seal]" % self.server, self.lp, self.machine_creds) # # Create the machine account def create_machine_account(self): self.machine_pass = samba.generate_random_password(32, 32) self.machine_name = MACHINE_NAME self.machine_dn = "cn=%s,%s" % (self.machine_name, self.ldb.domain_dn()) # remove the account if it exists, this will happen if a previous test # run failed delete_force(self.ldb, self.machine_dn) utf16pw = unicode( '"' + self.machine_pass.encode('utf-8') + '"', 'utf-8' ).encode('utf-16-le') self.ldb.add({ "dn": self.machine_dn, "objectclass": "computer", "sAMAccountName": "%s$" % self.machine_name, "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), "unicodePwd": utf16pw}) self.machine_creds = Credentials() self.machine_creds.guess(self.get_loadparm()) self.machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA) self.machine_creds.set_kerberos_state(DONT_USE_KERBEROS) self.machine_creds.set_password(self.machine_pass) self.machine_creds.set_username(self.machine_name + "$") self.machine_creds.set_workstation(self.machine_name) # # Create a test user account def create_user_account(self): self.user_pass = samba.generate_random_password(32, 32) self.user_name = USER_NAME self.user_dn = "cn=%s,%s" % (self.user_name, self.ldb.domain_dn()) # remove the account if it exists, this will happen if a previous test # run failed delete_force(self.ldb, self.user_dn) utf16pw = unicode( '"' + self.user_pass.encode('utf-8') + '"', 'utf-8' ).encode('utf-16-le') self.ldb.add({ "dn": self.user_dn, "objectclass": "user", "sAMAccountName": "%s" % self.user_name, "userAccountControl": str(UF_NORMAL_ACCOUNT), "unicodePwd": utf16pw}) self.user_creds = Credentials() self.user_creds.guess(self.get_loadparm()) self.user_creds.set_password(self.user_pass) self.user_creds.set_username(self.user_name) self.user_creds.set_workstation(self.machine_name) pass # # Get the authenticator from the machine creds. def get_authenticator(self, c): auth = self.machine_creds.new_client_authenticator(); current = netr_Authenticator() current.cred.data = [ord(x) for x in auth["credential"]] current.timestamp = auth["timestamp"] subsequent = netr_Authenticator() return (current, subsequent) def do_NetrLogonSamLogonWithFlags(self, c, current, subsequent): logon = samlogon_logon_info(self.domain, self.machine_name, self.user_creds) logon_level = netlogon.NetlogonNetworkTransitiveInformation validation_level = netlogon.NetlogonValidationSamInfo4 netr_flags = 0 c.netr_LogonSamLogonWithFlags(self.server, self.user_creds.get_workstation(), current, subsequent, logon_level, logon, validation_level, netr_flags) def do_NetrLogonGetDomainInfo(self, c, current, subsequent): query = netr_WorkstationInformation() c.netr_LogonGetDomainInfo(self.server, self.user_creds.get_workstation(), current, subsequent, 2, query)
class BasePasswordTestCase(PasswordTestCase): def _open_samr_user(self, res): self.assertTrue("objectSid" in res[0]) (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split() self.assertEquals(self.domain_sid, domain_sid) return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid) def _check_attribute(self, res, name, value): if value is None: self.assertTrue(name not in res[0], msg="attr[%s]=%r on dn[%s]" % (name, res[0], res[0].dn)) return if isinstance(value, tuple): (mode, value) = value else: mode = "equal" if mode == "ignore": return if mode == "absent": self.assertFalse(name in res[0], msg="attr[%s] not missing on dn[%s]" % (name, res[0].dn)) return self.assertTrue(name in res[0], msg="attr[%s] missing on dn[%s]" % (name, res[0].dn)) self.assertTrue(len(res[0][name]) == 1, msg="attr[%s]=%r on dn[%s]" % (name, res[0][name], res[0].dn)) print("%s = '%s'" % (name, res[0][name][0])) if mode == "present": return if mode == "equal": v = int(res[0][name][0]) value = int(value) msg = ("attr[%s]=[%s] != [%s] on dn[%s]\n" "(diff %d; actual value is %s than expected)" % (name, v, value, res[0].dn, v - value, ('less' if v < value else 'greater'))) self.assertTrue(v == value, msg) return if mode == "greater": v = int(res[0][name][0]) self.assertTrue(v > int(value), msg="attr[%s]=[%s] <= [%s] on dn[%s] (diff %d)" % (name, v, int(value), res[0].dn, v - int(value))) return if mode == "less": v = int(res[0][name][0]) self.assertTrue(v < int(value), msg="attr[%s]=[%s] >= [%s] on dn[%s] (diff %d)" % (name, v, int(value), res[0].dn, v - int(value))) return self.assertEqual(mode, not mode, "Invalid Mode[%s]" % mode) def _check_account_initial(self, userdn): self._check_account(userdn, badPwdCount=0, badPasswordTime=0, logonCount=0, lastLogon=0, lastLogonTimestamp=("absent", None), userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) def _check_account(self, dn, badPwdCount=None, badPasswordTime=None, logonCount=None, lastLogon=None, lastLogonTimestamp=None, lockoutTime=None, userAccountControl=None, msDSUserAccountControlComputed=None, effective_bad_password_count=None, msg=None, badPwdCountOnly=False): print('-=' * 36) if msg is not None: print("\033[01;32m %s \033[00m\n" % msg) attrs = [ "objectSid", "badPwdCount", "badPasswordTime", "lastLogon", "lastLogonTimestamp", "logonCount", "lockoutTime", "userAccountControl", "msDS-User-Account-Control-Computed" ] # in order to prevent some time resolution problems we sleep for # 10 micro second time.sleep(0.01) res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertTrue(len(res) == 1) self._check_attribute(res, "badPwdCount", badPwdCount) self._check_attribute(res, "lockoutTime", lockoutTime) self._check_attribute(res, "badPasswordTime", badPasswordTime) if not badPwdCountOnly: self._check_attribute(res, "logonCount", logonCount) self._check_attribute(res, "lastLogon", lastLogon) self._check_attribute(res, "lastLogonTimestamp", lastLogonTimestamp) self._check_attribute(res, "userAccountControl", userAccountControl) self._check_attribute(res, "msDS-User-Account-Control-Computed", msDSUserAccountControlComputed) lastLogon = int(res[0]["lastLogon"][0]) logonCount = int(res[0]["logonCount"][0]) samr_user = self._open_samr_user(res) uinfo3 = self.samr.QueryUserInfo(samr_user, 3) uinfo5 = self.samr.QueryUserInfo(samr_user, 5) uinfo16 = self.samr.QueryUserInfo(samr_user, 16) uinfo21 = self.samr.QueryUserInfo(samr_user, 21) self.samr.Close(samr_user) expected_acb_info = 0 if not badPwdCountOnly: if userAccountControl & dsdb.UF_NORMAL_ACCOUNT: expected_acb_info |= samr.ACB_NORMAL if userAccountControl & dsdb.UF_ACCOUNTDISABLE: expected_acb_info |= samr.ACB_DISABLED if userAccountControl & dsdb.UF_PASSWD_NOTREQD: expected_acb_info |= samr.ACB_PWNOTREQ if msDSUserAccountControlComputed & dsdb.UF_LOCKOUT: expected_acb_info |= samr.ACB_AUTOLOCK if msDSUserAccountControlComputed & dsdb.UF_PASSWORD_EXPIRED: expected_acb_info |= samr.ACB_PW_EXPIRED self.assertEquals(uinfo3.acct_flags, expected_acb_info) self.assertEquals(uinfo3.last_logon, lastLogon) self.assertEquals(uinfo3.logon_count, logonCount) expected_bad_password_count = 0 if badPwdCount is not None: expected_bad_password_count = badPwdCount if effective_bad_password_count is None: effective_bad_password_count = expected_bad_password_count self.assertEquals(uinfo3.bad_password_count, expected_bad_password_count) if not badPwdCountOnly: self.assertEquals(uinfo5.acct_flags, expected_acb_info) self.assertEquals(uinfo5.bad_password_count, effective_bad_password_count) self.assertEquals(uinfo5.last_logon, lastLogon) self.assertEquals(uinfo5.logon_count, logonCount) self.assertEquals(uinfo16.acct_flags, expected_acb_info) self.assertEquals(uinfo21.acct_flags, expected_acb_info) self.assertEquals(uinfo21.bad_password_count, effective_bad_password_count) self.assertEquals(uinfo21.last_logon, lastLogon) self.assertEquals(uinfo21.logon_count, logonCount) # check LDAP again and make sure the samr.QueryUserInfo # doesn't have any impact. res2 = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs) self.assertEquals(res[0], res2[0]) # in order to prevent some time resolution problems we sleep for # 10 micro second time.sleep(0.01) return res def update_lockout_settings(self, threshold, duration, observation_window): """Updates the global user lockout settings""" m = Message() m.dn = Dn(self.ldb, self.base_dn) account_lockout_duration_ticks = -int(duration * (1e7)) m["lockoutDuration"] = MessageElement(str(account_lockout_duration_ticks), FLAG_MOD_REPLACE, "lockoutDuration") m["lockoutThreshold"] = MessageElement(str(threshold), FLAG_MOD_REPLACE, "lockoutThreshold") lockout_observation_window_ticks = -int(observation_window * (1e7)) m["lockOutObservationWindow"] = MessageElement(str(lockout_observation_window_ticks), FLAG_MOD_REPLACE, "lockOutObservationWindow") self.ldb.modify(m) def _readd_user(self, creds, lockOutObservationWindow=0): username = creds.get_username() userpass = creds.get_password() userdn = "cn=%s,cn=users,%s" % (username, self.base_dn) delete_force(self.ldb, userdn) self.ldb.add({ "dn": userdn, "objectclass": "user", "sAMAccountName": username}) self.addCleanup(delete_force, self.ldb, userdn) # 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: """ + userdn + """ changetype: modify delete: userPassword add: userPassword userPassword: """ + userpass + """ """) # Enables the user account self.ldb.enable_account("(sAMAccountName=%s)" % username) use_kerberos = creds.get_kerberos_state() fail_creds = self.insta_creds(self.template_creds, username=username, userpass=userpass+"X", kerberos_state=use_kerberos) self._check_account_initial(userdn) # Fail once to get a badPasswordTime try: ldb = SamDB(url=self.host_url, credentials=fail_creds, lp=self.lp) self.fail() except LdbError as e: (num, msg) = e.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) # Succeed to reset everything to 0 ldb = SamDB(url=self.host_url, credentials=creds, lp=self.lp) return ldb def assertLoginFailure(self, url, creds, lp, errno=ERR_INVALID_CREDENTIALS): try: ldb = SamDB(url=url, credentials=creds, lp=lp) self.fail("Login unexpectedly succeeded") except LdbError as e1: (num, msg) = e1.args if errno is not None: self.assertEquals(num, errno, ("Login failed in the wrong way" "(got err %d, expected %d)" % (num, errno))) def setUp(self): super(BasePasswordTestCase, self).setUp() self.global_creds.set_gensec_features(self.global_creds.get_gensec_features() | gensec.FEATURE_SEAL) self.template_creds = Credentials() self.template_creds.set_username("testuser") self.template_creds.set_password("thatsAcomplPASS1") self.template_creds.set_domain(self.global_creds.get_domain()) self.template_creds.set_realm(self.global_creds.get_realm()) self.template_creds.set_workstation(self.global_creds.get_workstation()) self.template_creds.set_gensec_features(self.global_creds.get_gensec_features()) self.template_creds.set_kerberos_state(self.global_creds.get_kerberos_state()) # Gets back the basedn base_dn = self.ldb.domain_dn() # Gets back the configuration basedn configuration_dn = self.ldb.get_config_basedn().get_linearized() res = self.ldb.search(base_dn, scope=SCOPE_BASE, attrs=["lockoutDuration", "lockOutObservationWindow", "lockoutThreshold"]) if "lockoutDuration" in res[0]: lockoutDuration = res[0]["lockoutDuration"][0] else: lockoutDuration = 0 if "lockoutObservationWindow" in res[0]: lockoutObservationWindow = res[0]["lockoutObservationWindow"][0] else: lockoutObservationWindow = 0 if "lockoutThreshold" in res[0]: lockoutThreshold = res[0]["lockoutThreshold"][0] else: lockoutTreshold = 0 self.addCleanup(self.ldb.modify_ldif, """ dn: """ + base_dn + """ changetype: modify replace: lockoutDuration lockoutDuration: """ + str(lockoutDuration) + """ replace: lockoutObservationWindow lockoutObservationWindow: """ + str(lockoutObservationWindow) + """ replace: lockoutThreshold lockoutThreshold: """ + str(lockoutThreshold) + """ """) self.base_dn = self.ldb.domain_dn() self.account_lockout_duration = 2 self.lockout_observation_window = 2 self.update_lockout_settings(threshold=3, duration=2, observation_window=2) # update DC to allow password changes for the duration of this test self.allow_password_changes() self.domain_sid = security.dom_sid(self.ldb.get_domain_sid()) self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % self.host, self.lp, self.global_creds) self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid) self.addCleanup(self.delete_ldb_connections) # (Re)adds the test user accounts self.lockout1krb5_creds = self.insta_creds(self.template_creds, username="******", userpass="******", kerberos_state=MUST_USE_KERBEROS) self.lockout1krb5_ldb = self._readd_user(self.lockout1krb5_creds) self.lockout1ntlm_creds = self.insta_creds(self.template_creds, username="******", userpass="******", kerberos_state=DONT_USE_KERBEROS) self.lockout1ntlm_ldb = self._readd_user(self.lockout1ntlm_creds) def delete_ldb_connections(self): del self.lockout1krb5_ldb del self.lockout1ntlm_ldb del self.ldb def tearDown(self): super(BasePasswordTestCase, self).tearDown() def _test_login_lockout(self, creds): username = creds.get_username() userpass = creds.get_password() userdn = "cn=%s,cn=users,%s" % (username, self.base_dn) use_kerberos = creds.get_kerberos_state() # This unlocks by waiting for account_lockout_duration if use_kerberos == MUST_USE_KERBEROS: logoncount_relation = 'greater' lastlogon_relation = 'greater' print("Performs a lockout attempt against LDAP using Kerberos") else: logoncount_relation = 'equal' lastlogon_relation = 'equal' print("Performs a lockout attempt against LDAP using NTLM") # Change password on a connection as another user res = self._check_account(userdn, badPwdCount=0, badPasswordTime=("greater", 0), logonCount=(logoncount_relation, 0), lastLogon=("greater", 0), lastLogonTimestamp=("greater", 0), userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) logonCount = int(res[0]["logonCount"][0]) lastLogon = int(res[0]["lastLogon"][0]) firstLogon = lastLogon lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0]) print(firstLogon) print(lastLogonTimestamp) self.assertGreater(lastLogon, badPasswordTime) self.assertGreaterEqual(lastLogon, lastLogonTimestamp) # Open a second LDB connection with the user credentials. Use the # command line credentials for informations like the domain, the realm # and the workstation. creds_lockout = self.insta_creds(creds) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") self.assertLoginFailure(self.host_url, creds_lockout, self.lp) res = self._check_account(userdn, badPwdCount=1, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0, msg='lastlogontimestamp with wrong password') badPasswordTime = int(res[0]["badPasswordTime"][0]) # Correct old password creds_lockout.set_password(userpass) ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) # lastLogonTimestamp should not change # lastLogon increases if badPwdCount is non-zero (!) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=badPasswordTime, logonCount=(logoncount_relation, logonCount), lastLogon=('greater', lastLogon), lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0, msg='LLTimestamp is updated to lastlogon') logonCount = int(res[0]["logonCount"][0]) lastLogon = int(res[0]["lastLogon"][0]) self.assertGreater(lastLogon, badPasswordTime) self.assertGreaterEqual(lastLogon, lastLogonTimestamp) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") self.assertLoginFailure(self.host_url, creds_lockout, self.lp) res = self._check_account(userdn, badPwdCount=1, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e2: (num, msg) = e2.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=2, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) print("two failed password change") # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e3: (num, msg) = e3.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=3, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, lockoutTime=("greater", badPasswordTime), userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=dsdb.UF_LOCKOUT) badPasswordTime = int(res[0]["badPasswordTime"][0]) lockoutTime = int(res[0]["lockoutTime"][0]) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e4: (num, msg) = e4.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=3, badPasswordTime=badPasswordTime, logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, lockoutTime=lockoutTime, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=dsdb.UF_LOCKOUT) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e5: (num, msg) = e5.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=3, badPasswordTime=badPasswordTime, logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, lockoutTime=lockoutTime, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=dsdb.UF_LOCKOUT) # The correct password, but we are locked out creds_lockout.set_password(userpass) try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e6: (num, msg) = e6.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=3, badPasswordTime=badPasswordTime, logonCount=logonCount, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, lockoutTime=lockoutTime, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=dsdb.UF_LOCKOUT) # wait for the lockout to end time.sleep(self.account_lockout_duration + 1) print(self.account_lockout_duration + 1) res = self._check_account(userdn, badPwdCount=3, effective_bad_password_count=0, badPasswordTime=badPasswordTime, logonCount=logonCount, lockoutTime=lockoutTime, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) # The correct password after letting the timeout expire creds_lockout.set_password(userpass) creds_lockout2 = self.insta_creds(creds_lockout) ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout2, lp=self.lp) time.sleep(3) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=badPasswordTime, logonCount=(logoncount_relation, logonCount), lastLogon=(lastlogon_relation, lastLogon), lastLogonTimestamp=lastLogonTimestamp, lockoutTime=0, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0, msg="lastLogon is way off") logonCount = int(res[0]["logonCount"][0]) lastLogon = int(res[0]["lastLogon"][0]) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e7: (num, msg) = e7.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=1, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lockoutTime=0, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e8: (num, msg) = e8.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=2, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lockoutTime=0, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) time.sleep(self.lockout_observation_window + 1) res = self._check_account(userdn, badPwdCount=2, effective_bad_password_count=0, badPasswordTime=badPasswordTime, logonCount=logonCount, lockoutTime=0, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) # The wrong password creds_lockout.set_password("thatsAcomplPASS1x") try: ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) self.fail() except LdbError as e9: (num, msg) = e9.args self.assertEquals(num, ERR_INVALID_CREDENTIALS) res = self._check_account(userdn, badPwdCount=1, badPasswordTime=("greater", badPasswordTime), logonCount=logonCount, lockoutTime=0, lastLogon=lastLogon, lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) # The correct password without letting the timeout expire creds_lockout.set_password(userpass) ldb_lockout = SamDB(url=self.host_url, credentials=creds_lockout, lp=self.lp) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=badPasswordTime, logonCount=(logoncount_relation, logonCount), lockoutTime=0, lastLogon=("greater", lastLogon), lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) def _test_multiple_logon(self, creds): # Test the happy case in which a user logs on correctly, then # logs on correctly again, so that the bad password and # lockout times are both zero the second time. The lastlogon # time should increase. # Open a second LDB connection with the user credentials. Use the # command line credentials for informations like the domain, the realm # and the workstation. username = creds.get_username() userdn = "cn=%s,cn=users,%s" % (username, self.base_dn) use_kerberos = creds.get_kerberos_state() if use_kerberos == MUST_USE_KERBEROS: print("Testing multiple logon with Kerberos") logoncount_relation = 'greater' lastlogon_relation = 'greater' else: print("Testing multiple logon with NTLM") logoncount_relation = 'equal' lastlogon_relation = 'equal' SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=("greater", 0), logonCount=(logoncount_relation, 0), lastLogon=("greater", 0), lastLogonTimestamp=("greater", 0), userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0) badPasswordTime = int(res[0]["badPasswordTime"][0]) logonCount = int(res[0]["logonCount"][0]) lastLogon = int(res[0]["lastLogon"][0]) lastLogonTimestamp = int(res[0]["lastLogonTimestamp"][0]) firstLogon = lastLogon print("last logon is %d" % lastLogon) self.assertGreater(lastLogon, badPasswordTime) self.assertGreaterEqual(lastLogon, lastLogonTimestamp) time.sleep(1) SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=badPasswordTime, logonCount=(logoncount_relation, logonCount), lastLogon=(lastlogon_relation, lastLogon), lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0, msg=("second logon, firstlogon was %s" % firstLogon)) lastLogon = int(res[0]["lastLogon"][0]) time.sleep(1) SamDB(url=self.host_url, credentials=self.insta_creds(creds), lp=self.lp) res = self._check_account(userdn, badPwdCount=0, badPasswordTime=badPasswordTime, logonCount=(logoncount_relation, logonCount), lastLogon=(lastlogon_relation, lastLogon), lastLogonTimestamp=lastLogonTimestamp, userAccountControl= dsdb.UF_NORMAL_ACCOUNT, msDSUserAccountControlComputed=0)
class RodcCmdTestCase(SambaToolCmdTest): def setUp(self): super(RodcCmdTestCase, self).setUp() self.lp = samba.param.LoadParm() self.lp.load(os.environ["SMB_CONF_PATH"]) self.creds = Credentials() self.creds.set_username(os.environ["DC_USERNAME"]) self.creds.set_password(os.environ["DC_PASSWORD"]) self.creds.guess(self.lp) self.session = system_session() self.ldb = SamDB("ldap://" + os.environ["DC_SERVER"], session_info=self.session, credentials=self.creds,lp=self.lp) self.base_dn = self.ldb.domain_dn() self.ldb.newuser("sambatool1", "1qazXSW@") self.ldb.newuser("sambatool2", "2wsxCDE#") self.ldb.newuser("sambatool3", "3edcVFR$") self.ldb.newuser("sambatool4", "4rfvBGT%") self.ldb.newuser("sambatool5", "5tjbNHY*") self.ldb.newuser("sambatool6", "6yknMJU*") self.ldb.add_remove_group_members("Allowed RODC Password Replication Group", ["sambatool1", "sambatool2", "sambatool3", "sambatool4", "sambatool5"], add_members_operation=True) def tearDown(self): super(RodcCmdTestCase, self).tearDown() self.ldb.deleteuser("sambatool1") self.ldb.deleteuser("sambatool2") self.ldb.deleteuser("sambatool3") self.ldb.deleteuser("sambatool4") self.ldb.deleteuser("sambatool5") self.ldb.deleteuser("sambatool6") (result, out, err) = self.runsubcmd("drs", "replicate", "--local", "unused", os.environ["DC_SERVER"], self.base_dn) def test_single_by_account_name(self): (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool1", "--server", os.environ["DC_SERVER"]) self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\n" % self.base_dn) self.assertEqual(err, "") def test_single_by_dn(self): (result, out, err) = self.runsubcmd("rodc", "preload", "cn=sambatool2,cn=users,%s" % self.base_dn, "--server", os.environ["DC_SERVER"]) self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertEqual(out, "Replicating DN CN=sambatool2,CN=Users,%s\n" % self.base_dn) def test_multi_by_account_name(self): (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool1", "sambatool2", "--server", os.environ["DC_SERVER"]) self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\nReplicating DN CN=sambatool2,CN=Users,%s\n" % (self.base_dn, self.base_dn)) def test_multi_by_dn(self): (result, out, err) = self.runsubcmd("rodc", "preload", "cn=sambatool3,cn=users,%s" % self.base_dn, "cn=sambatool4,cn=users,%s" % self.base_dn, "--server", os.environ["DC_SERVER"]) self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertEqual(out, "Replicating DN CN=sambatool3,CN=Users,%s\nReplicating DN CN=sambatool4,CN=Users,%s\n" % (self.base_dn, self.base_dn)) def test_multi_in_file(self): tempf = os.path.join(self.tempdir, "accountlist") open(tempf, 'w').write("sambatool1\nsambatool2") (result, out, err) = self.runsubcmd("rodc", "preload", "--file", tempf, "--server", os.environ["DC_SERVER"]) self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertEqual(out, "Replicating DN CN=sambatool1,CN=Users,%s\nReplicating DN CN=sambatool2,CN=Users,%s\n" % (self.base_dn, self.base_dn)) os.unlink(tempf) def test_multi_with_missing_name_success(self): (result, out, err) = self.runsubcmd("rodc", "preload", "nonexistentuser1", "sambatool5", "nonexistentuser2", "--server", os.environ["DC_SERVER"], "--ignore-errors") self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertTrue(out.startswith("Replicating DN CN=sambatool5,CN=Users,%s\n" % self.base_dn)) def test_multi_with_missing_name_failure(self): (result, out, err) = self.runsubcmd("rodc", "preload", "nonexistentuser1", "sambatool5", "nonexistentuser2", "--server", os.environ["DC_SERVER"]) self.assertCmdFail(result, "ensuring rodc prefetch quit on missing user") def test_multi_without_group_success(self): (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool6", "sambatool5", "--server", os.environ["DC_SERVER"], "--ignore-errors") self.assertCmdSuccess(result, out, err, "ensuring rodc prefetch ran successfully") self.assertTrue(out.startswith("Replicating DN CN=sambatool6,CN=Users,%s\n" "Replicating DN CN=sambatool5,CN=Users,%s\n" % (self.base_dn, self.base_dn))) def test_multi_without_group_failure(self): (result, out, err) = self.runsubcmd("rodc", "preload", "sambatool6", "sambatool5", "--server", os.environ["DC_SERVER"]) self.assertCmdFail(result, "ensuring rodc prefetch quit on non-replicated user")
class CredentialsOptions(optparse.OptionGroup): """Command line options for specifying credentials.""" def __init__(self, parser, special_name=None): self.special_name = special_name if special_name is not None: self.section = "Credentials Options (%s)" % special_name else: self.section = "Credentials Options" self.ask_for_password = True self.ipaddress = None self.machine_pass = False optparse.OptionGroup.__init__(self, parser, self.section) self._add_option("--simple-bind-dn", metavar="DN", action="callback", callback=self._set_simple_bind_dn, type=str, help="DN to use for a simple bind") self._add_option("--password", metavar="PASSWORD", action="callback", help="Password", type=str, callback=self._set_password) self._add_option("-U", "--username", metavar="USERNAME", action="callback", type=str, help="Username", callback=self._parse_username) self._add_option("-W", "--workgroup", metavar="WORKGROUP", action="callback", type=str, help="Workgroup", callback=self._parse_workgroup) self._add_option("-N", "--no-pass", action="callback", help="Don't ask for a password", callback=self._set_no_password) self._add_option("-k", "--kerberos", metavar="KERBEROS", action="callback", type=str, help="Use Kerberos", callback=self._set_kerberos) self._add_option("", "--ipaddress", metavar="IPADDRESS", action="callback", type=str, help="IP address of server", callback=self._set_ipaddress) self._add_option("-P", "--machine-pass", action="callback", help="Use stored machine account password", callback=self._set_machine_pass) self._add_option("--krb5-ccache", metavar="KRB5CCNAME", action="callback", type=str, help="Kerberos Credentials cache", callback=self._set_krb5_ccache) self.creds = Credentials() def _add_option(self, *args1, **kwargs): if self.special_name is None: return self.add_option(*args1, **kwargs) args2 = () for a in args1: if not a.startswith("--"): continue args2 += (a.replace("--", "--%s-" % self.special_name),) self.add_option(*args2, **kwargs) def _parse_username(self, option, opt_str, arg, parser): self.creds.parse_string(arg) self.machine_pass = False def _parse_workgroup(self, option, opt_str, arg, parser): self.creds.set_domain(arg) def _set_password(self, option, opt_str, arg, parser): self.creds.set_password(arg) self.ask_for_password = False self.machine_pass = False def _set_no_password(self, option, opt_str, arg, parser): self.ask_for_password = False def _set_machine_pass(self, option, opt_str, arg, parser): self.machine_pass = True def _set_ipaddress(self, option, opt_str, arg, parser): self.ipaddress = arg def _set_kerberos(self, option, opt_str, arg, parser): self.creds.set_kerberos_state(parse_kerberos_arg(arg, opt_str)) def _set_simple_bind_dn(self, option, opt_str, arg, parser): self.creds.set_bind_dn(arg) def _set_krb5_ccache(self, option, opt_str, arg, parser): self.creds.set_named_ccache(arg) def get_credentials(self, lp, fallback_machine=False): """Obtain the credentials set on the command-line. :param lp: Loadparm object to use. :return: Credentials object """ self.creds.guess(lp) if self.machine_pass: self.creds.set_machine_account(lp) elif self.ask_for_password: self.creds.set_cmdline_callbacks() # possibly fallback to using the machine account, if we have # access to the secrets db if fallback_machine and not self.creds.authentication_requested(): try: self.creds.set_machine_account(lp) except Exception: pass return self.creds
class LDAPBackend(ProvisionBackend): def __init__(self, backend_type, paths=None, lp=None, names=None, logger=None, domainsid=None, schema=None, hostname=None, ldapadminpass=None, slapd_path=None, ldap_backend_extra_port=None, ldap_backend_forced_uri=None, ldap_dryrun_mode=False): super(LDAPBackend, self).__init__(backend_type=backend_type, paths=paths, lp=lp, names=names, logger=logger) self.domainsid = domainsid self.schema = schema self.hostname = hostname self.ldapdir = os.path.join(paths.private_dir, "ldap") self.ldapadminpass = ldapadminpass self.slapd_path = slapd_path self.slapd_command = None self.slapd_command_escaped = None self.slapd_pid = os.path.join(self.ldapdir, "slapd.pid") self.ldap_backend_extra_port = ldap_backend_extra_port self.ldap_dryrun_mode = ldap_dryrun_mode if ldap_backend_forced_uri is not None: self.ldap_uri = ldap_backend_forced_uri else: self.ldap_uri = "ldapi://%s" % urllib_quote( os.path.join(self.ldapdir, "ldapi"), safe="") if not os.path.exists(self.ldapdir): os.mkdir(self.ldapdir) def init(self): from samba.provision import ProvisioningError # we will shortly start slapd with ldapi for final provisioning. first # check with ldapsearch -> rootDSE via self.ldap_uri if another # instance of slapd is already running try: ldapi_db = Ldb(self.ldap_uri) ldapi_db.search(base="", scope=SCOPE_BASE, expression="(objectClass=OpenLDAProotDSE)") try: f = open(self.slapd_pid, "r") except IOError as err: if err != errno.ENOENT: raise else: try: p = f.read() finally: f.close() self.logger.info("Check for slapd process with PID: %s and terminate it manually." % p) raise SlapdAlreadyRunning(self.ldap_uri) except LdbError: # XXX: We should never be catching all Ldb errors pass # Try to print helpful messages when the user has not specified the # path to slapd if self.slapd_path is None: raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!") if not os.path.exists(self.slapd_path): self.logger.warning("Path (%s) to slapd does not exist!", self.slapd_path) if not os.path.isdir(self.ldapdir): os.makedirs(self.ldapdir, 0o700) # Put the LDIF of the schema into a database so we can search on # it to generate schema-dependent configurations in Fedora DS and # OpenLDAP schemadb_path = os.path.join(self.ldapdir, "schema-tmp.ldb") try: os.unlink(schemadb_path) except OSError: pass self.schema.write_to_tmp_ldb(schemadb_path) self.credentials = Credentials() self.credentials.guess(self.lp) # Kerberos to an ldapi:// backend makes no sense (we also force EXTERNAL) self.credentials.set_kerberos_state(DONT_USE_KERBEROS) self.credentials.set_username("samba-admin") self.credentials.set_password(self.ldapadminpass) self.credentials.set_forced_sasl_mech("EXTERNAL") self.provision() def provision(self): pass def start(self): from samba.provision import ProvisioningError self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'" ldap_backend_script = os.path.join(self.ldapdir, "ldap_backend_startup.sh") f = open(ldap_backend_script, 'w') try: f.write("#!/bin/sh\n" + self.slapd_command_escaped + " $@\n") finally: f.close() os.chmod(ldap_backend_script, 0o755) # Now start the slapd, so we can provision onto it. We keep the # subprocess context around, to kill this off at the successful # end of the script self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False) count = 0 while self.slapd.poll() is None: # Wait until the socket appears try: time.sleep(1) ldapi_db = Ldb(self.ldap_uri, lp=self.lp, credentials=self.credentials) ldapi_db.search(base="", scope=SCOPE_BASE, expression="(objectClass=OpenLDAProotDSE)") # If we have got here, then we must have a valid connection to # the LDAP server! return except LdbError: count = count + 1 if count > 15: self.logger.error("Could not connect to slapd started with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") raise ProvisioningError("slapd never accepted a connection within 15 seconds of starting") self.logger.error("Could not start slapd with: %s" % "\'" + "\' \'".join(self.slapd_provision_command) + "\'") raise ProvisioningError("slapd died before we could make a connection to it") def shutdown(self): # if an LDAP backend is in use, terminate slapd after final provision # and check its proper termination if self.slapd.poll() is None: # Kill the slapd if getattr(self.slapd, "terminate", None) is not None: self.slapd.terminate() else: # Older python versions don't have .terminate() import signal os.kill(self.slapd.pid, signal.SIGTERM) # and now wait for it to die self.slapd.communicate() def post_setup(self): return LDAPBackendResult(self.slapd_command_escaped, self.ldapdir)
def join_replicate(ctx): """Replicate the SAM.""" print "Starting replication" ctx.local_samdb.transaction_start() try: source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id()) if ctx.ntds_guid is None: print("Using DS_BIND_GUID_W2K3") destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3) else: destination_dsa_guid = ctx.ntds_guid if ctx.RODC: repl_creds = Credentials() repl_creds.guess(ctx.lp) repl_creds.set_kerberos_state(DONT_USE_KERBEROS) repl_creds.set_username(ctx.samname) repl_creds.set_password(ctx.acct_pass) else: repl_creds = ctx.creds binding_options = "seal" if int(ctx.lp.get("log level")) >= 5: binding_options += ",print" repl = drs_utils.drs_Replicate( "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options), ctx.lp, repl_creds, ctx.local_samdb) repl.replicate(ctx.schema_dn, source_dsa_invocation_id, destination_dsa_guid, schema=True, rodc=ctx.RODC, replica_flags=ctx.replica_flags) repl.replicate(ctx.config_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) if not ctx.subdomain: # Replicate first the critical object for the basedn if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY: print "Replicating critical objects from the base DN of the domain" ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC repl.replicate(ctx.base_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.domain_replica_flags) ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY | drsuapi.DRSUAPI_DRS_GET_ANC else: ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_GET_ANC repl.replicate(ctx.base_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.domain_replica_flags) print "Done with always replicated NC (base, config, schema)" for nc in (ctx.domaindns_zone, ctx.forestdns_zone): if nc in ctx.nc_list: print "Replicating %s" % (str(nc)) repl.replicate(nc, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) if 'DC=ForestDnsZones,%s' % ctx.root_dn in ctx.nc_list: repl.replicate('DC=ForestDnsZones,%s' % ctx.root_dn, source_dsa_invocation_id, destination_dsa_guid, rodc=ctx.RODC, replica_flags=ctx.replica_flags) # FIXME At this point we should add an entry in the forestdns and domaindns NC # (those under CN=Partions,DC=...) # in order to indicate that we hold a replica for this NC if ctx.RODC: repl.replicate(ctx.acct_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) ctx.repl = repl ctx.source_dsa_invocation_id = source_dsa_invocation_id ctx.destination_dsa_guid = destination_dsa_guid print "Committing SAM database" except: ctx.local_samdb.transaction_cancel() raise else: ctx.local_samdb.transaction_commit()
class PassWordHashLDAPTests(PassWordHashTests): def setUp(self): super(PassWordHashLDAPTests, self).setUp() # Get the supplemental credentials for the user under test def get_supplemental_creds_drs(self): binding_str = "ncacn_ip_tcp:%s[seal]" % os.environ["SERVER"] dn = "cn=" + USER_NAME + ",cn=users," + self.base_dn drs = drsuapi.drsuapi(binding_str, self.get_loadparm(), self.creds) (drs_handle, supported_extensions) = drs_utils.drs_DsBind(drs) req8 = drsuapi.DsGetNCChangesRequest8() null_guid = misc.GUID() req8.destination_dsa_guid = null_guid req8.source_dsa_invocation_id = null_guid req8.naming_context = drsuapi.DsReplicaObjectIdentifier() req8.naming_context.dn = unicode(dn) req8.highwatermark = drsuapi.DsReplicaHighWaterMark() req8.highwatermark.tmp_highest_usn = 0 req8.highwatermark.reserved_usn = 0 req8.highwatermark.highest_usn = 0 req8.uptodateness_vector = None req8.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | drsuapi.DRSUAPI_DRS_PER_SYNC | drsuapi.DRSUAPI_DRS_GET_ANC | drsuapi.DRSUAPI_DRS_NEVER_SYNCED | drsuapi.DRSUAPI_DRS_WRIT_REP) req8.max_object_count = 402 req8.max_ndr_size = 402116 req8.extended_op = drsuapi.DRSUAPI_EXOP_REPL_OBJ req8.fsmo_info = 0 req8.partial_attribute_set = None req8.partial_attribute_set_ex = None req8.mapping_ctr.num_mappings = 0 req8.mapping_ctr.mappings = None (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) obj_item = ctr.first_object obj = obj_item.object sc_blob = None for i in range(0, obj.attribute_ctr.num_attributes): attr = obj.attribute_ctr.attributes[i] if attid_equal(attr.attid, drsuapi.DRSUAPI_ATTID_supplementalCredentials): net_ctx = net.Net(self.creds) net_ctx.replicate_decrypt(drs, attr, 0) sc_blob = attr.value_ctr.values[0].blob sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob) return sc def test_wDigest_supplementalCredentials(self): self.creds = Credentials() self.creds.set_username(os.environ["USERNAME"]) self.creds.set_password(os.environ["PASSWORD"]) self.creds.guess(self.lp) ldb = SamDB("ldap://" + os.environ["SERVER"], credentials=self.creds, lp=self.lp) self.add_user(ldb=ldb) sc = self.get_supplemental_creds_drs() (pos, package) = get_package(sc, "Primary:WDigest") self.assertEquals("Primary:WDigest", package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(package.data)) self.check_wdigests(digests)
def upgrade_from_samba3(samba3, logger, targetdir, session_info=None, useeadb=False, dns_backend=None, use_ntvfs=False): """Upgrade from samba3 database to samba4 AD database :param samba3: samba3 object :param logger: Logger object :param targetdir: samba4 database directory :param session_info: Session information """ serverrole = samba3.lp.server_role() domainname = samba3.lp.get("workgroup") realm = samba3.lp.get("realm") netbiosname = samba3.lp.get("netbios name") if samba3.lp.get("ldapsam:trusted") is None: samba3.lp.set("ldapsam:trusted", "yes") # secrets db try: secrets_db = samba3.get_secrets_db() except IOError as e: raise ProvisioningError( "Could not open '%s', the Samba3 secrets database: %s. Perhaps you specified the incorrect smb.conf, --testparm or --dbdir option?" % (samba3.privatedir_path("secrets.tdb"), str(e)) ) if not domainname: domainname = secrets_db.domains()[0] logger.warning("No workgroup specified in smb.conf file, assuming '%s'", domainname) if not realm: if serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC": raise ProvisioningError( "No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (it is the DNS name of the AD domain you wish to create." ) else: realm = domainname.upper() logger.warning("No realm specified in smb.conf file, assuming '%s'", realm) # Find machine account and password next_rid = 1000 try: machinepass = secrets_db.get_machine_password(netbiosname) except KeyError: machinepass = None if samba3.lp.get("passdb backend").split(":")[0].strip() == "ldapsam": base_dn = samba3.lp.get("ldap suffix") ldapuser = samba3.lp.get("ldap admin dn") ldappass = secrets_db.get_ldap_bind_pw(ldapuser) if ldappass is None: raise ProvisioningError( "ldapsam passdb backend detected but no LDAP Bind PW found in secrets.tdb for user %s. Please point this tool at the secrets.tdb that was used by the previous installation." ) ldappass = ldappass.strip("\x00") ldap = True else: ldapuser = None ldappass = None ldap = False # We must close the direct pytdb database before the C code loads it secrets_db.close() # Connect to old password backend passdb.set_secrets_dir(samba3.lp.get("private dir")) s3db = samba3.get_sam_db() # Get domain sid try: domainsid = passdb.get_global_sam_sid() except passdb.error: raise Exception("Can't find domain sid for '%s', Exiting." % domainname) # Get machine account, sid, rid try: machineacct = s3db.getsampwnam("%s$" % netbiosname) except passdb.error: machinerid = None machinesid = None else: machinesid, machinerid = machineacct.user_sid.split() # Export account policy logger.info("Exporting account policy") policy = s3db.get_account_policy() # Export groups from old passdb backend logger.info("Exporting groups") grouplist = s3db.enum_group_mapping() groupmembers = {} for group in grouplist: sid, rid = group.sid.split() if sid == domainsid: if rid >= next_rid: next_rid = rid + 1 # Get members for each group/alias if group.sid_name_use == lsa.SID_NAME_ALIAS: try: members = s3db.enum_aliasmem(group.sid) groupmembers[str(group.sid)] = members except passdb.error as e: logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e) continue elif group.sid_name_use == lsa.SID_NAME_DOM_GRP: try: members = s3db.enum_group_members(group.sid) groupmembers[str(group.sid)] = members except passdb.error as e: logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e) continue elif group.sid_name_use == lsa.SID_NAME_WKN_GRP: (group_dom_sid, rid) = group.sid.split() if group_dom_sid != security.dom_sid(security.SID_BUILTIN): logger.warn( "Ignoring 'well known' group '%s' (should already be in AD, and have no members)", group.nt_name ) continue # A number of buggy databases mix up well known groups and aliases. try: members = s3db.enum_aliasmem(group.sid) groupmembers[str(group.sid)] = members except passdb.error as e: logger.warn("Ignoring group '%s' %s listed but then not found: %s", group.nt_name, group.sid, e) continue else: logger.warn("Ignoring group '%s' %s with sid_name_use=%d", group.nt_name, group.sid, group.sid_name_use) continue # Export users from old passdb backend logger.info("Exporting users") userlist = s3db.search_users(0) userdata = {} uids = {} admin_user = None for entry in userlist: if machinerid and machinerid == entry["rid"]: continue username = entry["account_name"] if entry["rid"] < 1000: logger.info(" Skipping wellknown rid=%d (for username=%s)", entry["rid"], username) continue if entry["rid"] >= next_rid: next_rid = entry["rid"] + 1 user = s3db.getsampwnam(username) acct_type = user.acct_ctrl & (samr.ACB_NORMAL | samr.ACB_WSTRUST | samr.ACB_SVRTRUST | samr.ACB_DOMTRUST) if acct_type == samr.ACB_SVRTRUST: logger.warn( " Demoting BDC account trust for %s, this DC must be elevated to an AD DC using 'samba-tool domain dcpromo'" % username[:-1] ) user.acct_ctrl = (user.acct_ctrl & ~samr.ACB_SVRTRUST) | samr.ACB_WSTRUST elif acct_type == samr.ACB_DOMTRUST: logger.warn( " Skipping inter-domain trust from domain %s, this trust must be re-created as an AD trust" % username[:-1] ) continue elif acct_type == (samr.ACB_WSTRUST) and username[-1] != "$": logger.warn( " Skipping account %s that has ACB_WSTRUST (W) set but does not end in $. This account can not have worked, and is probably left over from a misconfiguration." % username ) continue elif acct_type == (samr.ACB_NORMAL | samr.ACB_WSTRUST) and username[-1] == "$": logger.warn( " Fixing account %s which had both ACB_NORMAL (U) and ACB_WSTRUST (W) set. Account will be marked as ACB_WSTRUST (W), i.e. as a domain member" % username ) user.acct_ctrl = user.acct_ctrl & ~samr.ACB_NORMAL elif acct_type == (samr.ACB_NORMAL | samr.ACB_SVRTRUST) and username[-1] == "$": logger.warn( " Fixing account %s which had both ACB_NORMAL (U) and ACB_SVRTRUST (S) set. Account will be marked as ACB_WSTRUST (S), i.e. as a domain member" % username ) user.acct_ctrl = user.acct_ctrl & ~samr.ACB_NORMAL elif acct_type == 0 and username[-1] != "$": user.acct_ctrl = user.acct_ctrl | samr.ACB_NORMAL elif acct_type == samr.ACB_NORMAL or acct_type == samr.ACB_WSTRUST: pass else: raise ProvisioningError( """Failed to upgrade due to invalid account %s, account control flags 0x%08X must have exactly one of ACB_NORMAL (N, 0x%08X), ACB_WSTRUST (W 0x%08X), ACB_SVRTRUST (S 0x%08X) or ACB_DOMTRUST (D 0x%08X). Please fix this account before attempting to upgrade again """ % (username, user.acct_ctrl, samr.ACB_NORMAL, samr.ACB_WSTRUST, samr.ACB_SVRTRUST, samr.ACB_DOMTRUST) ) userdata[username] = user try: uids[username] = s3db.sid_to_id(user.user_sid)[0] except passdb.error: try: uids[username] = pwd.getpwnam(username).pw_uid except KeyError: pass if not admin_user and username.lower() == "root": admin_user = username if username.lower() == "administrator": admin_user = username try: group_memberships = s3db.enum_group_memberships(user) for group in group_memberships: if str(group) in groupmembers: if user.user_sid not in groupmembers[str(group)]: groupmembers[str(group)].append(user.user_sid) else: groupmembers[str(group)] = [user.user_sid] except passdb.error as e: logger.warn("Ignoring group memberships of '%s' %s: %s", username, user.user_sid, e) logger.info("Next rid = %d", next_rid) # Check for same username/groupname group_names = set([g.nt_name for g in grouplist]) user_names = set([u["account_name"] for u in userlist]) common_names = group_names.intersection(user_names) if common_names: logger.error("Following names are both user names and group names:") for name in common_names: logger.error(" %s" % name) raise ProvisioningError("Please remove common user/group names before upgrade.") # Check for same user sid/group sid group_sids = set([str(g.sid) for g in grouplist]) if len(grouplist) != len(group_sids): raise ProvisioningError("Please remove duplicate group sid entries before upgrade.") user_sids = set(["%s-%u" % (domainsid, u["rid"]) for u in userlist]) if len(userlist) != len(user_sids): raise ProvisioningError("Please remove duplicate user sid entries before upgrade.") common_sids = group_sids.intersection(user_sids) if common_sids: logger.error("Following sids are both user and group sids:") for sid in common_sids: logger.error(" %s" % str(sid)) raise ProvisioningError("Please remove duplicate sid entries before upgrade.") # Get posix attributes from ldap or the os homes = {} shells = {} pgids = {} if ldap: creds = Credentials() creds.guess(samba3.lp) creds.set_bind_dn(ldapuser) creds.set_password(ldappass) urls = samba3.lp.get("passdb backend").split(":", 1)[1].strip('"') for url in urls.split(): try: ldb_object = Ldb(url, credentials=creds) except ldb.LdbError as e: raise ProvisioningError("Could not open ldb connection to %s, the error message is: %s" % (url, e)) else: break logger.info("Exporting posix attributes") userlist = s3db.search_users(0) for entry in userlist: username = entry["account_name"] if username in uids.keys(): try: if ldap: homes[username] = get_posix_attr_from_ldap_backend( logger, ldb_object, base_dn, username, "homeDirectory" ) else: homes[username] = pwd.getpwnam(username).pw_dir except KeyError: pass except IndexError: pass try: if ldap: shells[username] = get_posix_attr_from_ldap_backend( logger, ldb_object, base_dn, username, "loginShell" ) else: shells[username] = pwd.getpwnam(username).pw_shell except KeyError: pass except IndexError: pass try: if ldap: pgids[username] = get_posix_attr_from_ldap_backend( logger, ldb_object, base_dn, username, "gidNumber" ) else: pgids[username] = pwd.getpwnam(username).pw_gid except KeyError: pass except IndexError: pass logger.info("Reading WINS database") samba3_winsdb = None try: samba3_winsdb = samba3.get_wins_db() except IOError as e: logger.warn("Cannot open wins database, Ignoring: %s", str(e)) if not (serverrole == "ROLE_DOMAIN_BDC" or serverrole == "ROLE_DOMAIN_PDC"): dns_backend = "NONE" # If we found an admin user, set a fake pw that we will override. # This avoids us printing out an admin password that we won't actually # set. if admin_user: adminpass = generate_random_password(12, 32) else: adminpass = None # Do full provision result = provision( logger, session_info, targetdir=targetdir, realm=realm, domain=domainname, domainsid=domainsid, next_rid=next_rid, dc_rid=machinerid, adminpass=adminpass, dom_for_fun_level=dsdb.DS_DOMAIN_FUNCTION_2003, hostname=netbiosname.lower(), machinepass=machinepass, serverrole=serverrole, samdb_fill=FILL_FULL, useeadb=useeadb, dns_backend=dns_backend, use_rfc2307=True, use_ntvfs=use_ntvfs, skip_sysvolacl=True, ) result.report_logger(logger) # Import WINS database logger.info("Importing WINS database") if samba3_winsdb: import_wins(Ldb(result.paths.winsdb), samba3_winsdb) # Set Account policy logger.info("Importing Account policy") import_sam_policy(result.samdb, policy, logger) # Migrate IDMAP database logger.info("Importing idmap database") import_idmap(result.idmap, samba3, logger) # Set the s3 context for samba4 configuration new_lp_ctx = s3param.get_context() new_lp_ctx.load(result.lp.configfile) new_lp_ctx.set("private dir", result.lp.get("private dir")) new_lp_ctx.set("state directory", result.lp.get("state directory")) new_lp_ctx.set("lock directory", result.lp.get("lock directory")) # Connect to samba4 backend s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend")) # Start a new transaction (should speed this up a little, due to index churn) result.samdb.transaction_start() logger.info("Adding groups") try: # Export groups to samba4 backend logger.info("Importing groups") for g in grouplist: # Ignore uninitialized groups (gid = -1) if g.gid != -1: add_group_from_mapping_entry(result.samdb, g, logger) add_ad_posix_idmap_entry(result.samdb, g.sid, g.gid, "ID_TYPE_GID", logger) add_posix_attrs( samdb=result.samdb, sid=g.sid, name=g.nt_name, nisdomain=domainname.lower(), xid_type="ID_TYPE_GID", logger=logger, ) except: # We need this, so that we do not give even more errors due to not cancelling the transaction result.samdb.transaction_cancel() raise logger.info("Committing 'add groups' transaction to disk") result.samdb.transaction_commit() logger.info("Adding users") # Start a new transaction (should speed this up a little, due to index churn) result.samdb.transaction_start() try: # Export users to samba4 backend logger.info("Importing users") for username in userdata: if username.lower() == "administrator": if userdata[username].user_sid != dom_sid(str(domainsid) + "-500"): logger.error( "User 'Administrator' in your existing directory has SID %s, expected it to be %s" % (userdata[username].user_sid, dom_sid(str(domainsid) + "-500")) ) raise ProvisioningError( "User 'Administrator' in your existing directory does not have SID ending in -500" ) if username.lower() == "root": if userdata[username].user_sid == dom_sid(str(domainsid) + "-500"): logger.warn("User root has been replaced by Administrator") else: logger.warn( "User root has been kept in the directory, it should be removed in favour of the Administrator user" ) s4_passdb.add_sam_account(userdata[username]) if username in uids: add_ad_posix_idmap_entry( result.samdb, userdata[username].user_sid, uids[username], "ID_TYPE_UID", logger ) if ( (username in homes) and (homes[username] is not None) and (username in shells) and (shells[username] is not None) and (username in pgids) and (pgids[username] is not None) ): add_posix_attrs( samdb=result.samdb, sid=userdata[username].user_sid, name=username, nisdomain=domainname.lower(), xid_type="ID_TYPE_UID", home=homes[username], shell=shells[username], pgid=pgids[username], logger=logger, ) except: # We need this, so that we do not give even more errors due to not cancelling the transaction result.samdb.transaction_cancel() raise logger.info("Committing 'add users' transaction to disk") result.samdb.transaction_commit() logger.info("Adding users to groups") # Start a new transaction (should speed this up a little, due to index churn) result.samdb.transaction_start() try: for g in grouplist: if str(g.sid) in groupmembers: add_users_to_group(result.samdb, g, groupmembers[str(g.sid)], logger) except: # We need this, so that we do not give even more errors due to not cancelling the transaction result.samdb.transaction_cancel() raise logger.info("Committing 'add users to groups' transaction to disk") result.samdb.transaction_commit() # Set password for administrator if admin_user: logger.info("Setting password for administrator") admin_userdata = s4_passdb.getsampwnam("administrator") admin_userdata.nt_passwd = userdata[admin_user].nt_passwd if userdata[admin_user].lanman_passwd: admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time if userdata[admin_user].pw_history: admin_userdata.pw_history = userdata[admin_user].pw_history s4_passdb.update_sam_account(admin_userdata) logger.info("Administrator password has been set to password of user '%s'", admin_user) if result.server_role == "active directory domain controller": setsysvolacl( result.samdb, result.paths.netlogon, result.paths.sysvol, result.paths.root_uid, result.paths.root_gid, security.dom_sid(result.domainsid), result.names.dnsdomain, result.names.domaindn, result.lp, use_ntvfs, )
common_sids = group_sids.intersection(user_sids) if common_sids: logger.error("Following sids are both user and group sids:") for sid in common_sids: logger.error(" %s" % str(sid)) raise ProvisioningError("Please remove duplicate sid entries before upgrade.") # Get posix attributes from ldap or the os homes = {} shells = {} pgids = {} if ldap: creds = Credentials() creds.guess(samba3.lp) creds.set_bind_dn(ldapuser) creds.set_password(ldappass) urls = samba3.lp.get("passdb backend").split(":",1)[1].strip('"') for url in urls.split(): try: ldb_object = Ldb(url, credentials=creds) except ldb.LdbError, e: raise ProvisioningError("Could not open ldb connection to %s, the error message is: %s" % (url, e)) else: break logger.info("Exporting posix attributes") userlist = s3db.search_users(0) for entry in userlist: username = entry['account_name'] if username in uids.keys(): try: if ldap:
class CredentialsOptions(optparse.OptionGroup): """Command line options for specifying credentials.""" def __init__(self, parser): self.no_pass = True self.ipaddress = None optparse.OptionGroup.__init__(self, parser, "Credentials Options") self.add_option("--simple-bind-dn", metavar="DN", action="callback", callback=self._set_simple_bind_dn, type=str, help="DN to use for a simple bind") self.add_option("--password", metavar="PASSWORD", action="callback", help="Password", type=str, callback=self._set_password) self.add_option("-U", "--username", metavar="USERNAME", action="callback", type=str, help="Username", callback=self._parse_username) self.add_option("-W", "--workgroup", metavar="WORKGROUP", action="callback", type=str, help="Workgroup", callback=self._parse_workgroup) self.add_option("-N", "--no-pass", action="store_true", help="Don't ask for a password") self.add_option("-k", "--kerberos", metavar="KERBEROS", action="callback", type=str, help="Use Kerberos", callback=self._set_kerberos) self.add_option("", "--ipaddress", metavar="IPADDRESS", action="callback", type=str, help="IP address of server", callback=self._set_ipaddress) self.creds = Credentials() def _parse_username(self, option, opt_str, arg, parser): self.creds.parse_string(arg) def _parse_workgroup(self, option, opt_str, arg, parser): self.creds.set_domain(arg) def _set_password(self, option, opt_str, arg, parser): self.creds.set_password(arg) self.no_pass = False def _set_ipaddress(self, option, opt_str, arg, parser): self.ipaddress = arg def _set_kerberos(self, option, opt_str, arg, parser): if arg.lower() in ["yes", 'true', '1']: self.creds.set_kerberos_state(MUST_USE_KERBEROS) elif arg.lower() in ["no", 'false', '0']: self.creds.set_kerberos_state(DONT_USE_KERBEROS) else: raise optparse.BadOptionErr("invalid kerberos option: %s" % arg) def _set_simple_bind_dn(self, option, opt_str, arg, parser): self.creds.set_bind_dn(arg) def get_credentials(self, lp, fallback_machine=False): """Obtain the credentials set on the command-line. :param lp: Loadparm object to use. :return: Credentials object """ self.creds.guess(lp) if self.no_pass: self.creds.set_cmdline_callbacks() # possibly fallback to using the machine account, if we have # access to the secrets db if fallback_machine and not self.creds.authentication_requested(): try: self.creds.set_machine_account(lp) except Exception: pass return self.creds
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_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])