Example #1
0
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
Example #2
0
    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
Example #3
0
 def _get_smb_connection(self, service='SysVol'):
     # Connect to SMB using kerberos
     parm = param.LoadParm()
     creds = Credentials()
     creds.set_kerberos_state(MUST_USE_KERBEROS)
     creds.guess(parm)
     conn = smb.SMB(self._get_server_name(), service, lp=parm, creds=creds)
     return conn
Example #4
0
    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()
Example #5
0
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_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
Example #7
0
 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
Example #8
0
 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)
Example #9
0
    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)
Example #10
0
 def _get_smb_connection(self, service='SysVol'):
     # Create options like if we were using command line
     parser = optparse.OptionParser()
     sambaopts = options.SambaOptions(parser)
     # Samba options
     parm = sambaopts.get_loadparm()
     s3_lp = s3param.get_context()
     s3_lp.load(parm.configfile)
     # Build credentials from credential options
     creds = Credentials()
     # Credentials need username and realm to be not empty strings to work
     creds.set_username('NOTEMPTY')
     creds.set_realm('NOTEMPTY')
     # Connect to SMB using kerberos
     creds.set_kerberos_state(MUST_USE_KERBEROS)
     # Create connection
     conn = libsmb.Conn(self._get_server_name(),
                        service,
                        lp=parm,
                        creds=creds,
                        sign=False)
     return conn
Example #11
0
def make_creds(username, password, kerberos_state=None):
    # use the global CREDS as a template
    c = Credentials()
    c.set_username(username)
    c.set_password(password)
    c.set_domain(CREDS.get_domain())
    c.set_realm(CREDS.get_realm())
    c.set_workstation(CREDS.get_workstation())

    if kerberos_state is None:
        kerberos_state = CREDS.get_kerberos_state()
    c.set_kerberos_state(kerberos_state)

    print '-' * 73
    if kerberos_state == MUST_USE_KERBEROS:
        print "we seem to be using kerberos for %s %s" % (username, password)
    elif kerberos_state == DONT_USE_KERBEROS:
        print "NOT using kerberos for %s %s" % (username, password)
    else:
        print "kerberos state is %s" % kerberos_state

    c.set_gensec_features(c.get_gensec_features() | gensec.FEATURE_SEAL)
    return c
Example #12
0
class ProvisionModel(BaseModel):

	def __init__(self,username,password):
		BaseModel.__init__(self,username,password);
		#if self.isAuthenticate():
		self.opts = Options();
		#self.creds = self.opts.get_credentials(self.opts.lp)
		self.creds = Credentials()
		self.creds.guess(self.opts.lp)
		self.creds.set_kerberos_state(DONT_USE_KERBEROS)
		self.session = system_session()

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

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

		
		try:
			provision(self.logger
					,self.session
					,self.creds
					, smbconf=self.opts.smbconf 
		#			,targetdir=self.opts.targetdir
					,samdb_fill=self.opts.samdb_fill
					,realm=self.opts.realm
					,domain=self.opts.domain,
		#			domainguid=self.opts.domain_guid, domainsid=self.opts.domain_sid,
		#			hostname=self.opts.host_name,
		#			hostip=self.opts.host_ip, hostip6=self.opts.host_ip6,
		#			ntdsguid=self.opts.ntds_guid,
		#			invocationid=self.opts.invocationid, 
					adminpass=self.opts.adminpass,
		#			krbtgtpass=self.opts.krbtgtpass, machinepass=self.opts.machinepass,
		#			dns_backend=self.opts.dns_backend,
		#			dnspass=self.opts.dnspass, root=self.opts.root, nobody=self.opts.nobody,
		#			wheel=self.opts.wheel, users=self.opts.users,
					serverrole=self.opts.server_role, dom_for_fun_level=self.opts.dom_for_fun_level,
		#			ldap_backend_extra_port=self.opts.ldap_backend_extra_port,
		#			ldap_backend_forced_uri=self.opts.ldap_backend_forced_uri,
		#			backend_type=self.opts.ldap_backend_type,
		#			ldapadminpass=self.opts.ldapadminpass, ol_mmr_urls=self.opts.ol_mmr_urls,
		#			slapd_path=self.opts.slapd_path, setup_ds_path=self.opts.setup_ds_path,
		#			nosync=self.opts.ldap_backend_nosync, ldap_dryrun_mode=self.opts.ldap_dryrun_mode,
					useeadb=self.opts.eadb, 
		#			next_rid=self.opts.next_rid,
					lp=self.opts.lp)
		#except ProvisioningError, e:
		except Exception,e:
			if(len(e.args)>1):
				self.SetError(e.args[1],e.args[0])
				self.logger.warning('Error: %s :%s',e.args[1], e.args[0])
			else:
				self.logger.warning('Error: %s :%s',e.args, 0)
				self.SetError(e.args,0)
			result = False;
			import traceback
			TraceBackLines = traceback.format_exc().splitlines()
			for t in TraceBackLines:
					self.logger.warning('Error: %s',t)
		LogOutput.flush();
		msg = "data: end provision\n\n"
		response.write(msg);
		return result;
Example #13
0
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
Example #14
0
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  = [x if isinstance(x, int) else ord(x) for x in os.urandom(DATA_LEN - pwd_len)]
        pwd = netlogon.netr_CryptPassword()
        pwd.length = pwd_len
        pwd.data = filler + [x if isinstance(x, int) else 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 = ('"%s"' % get_string(self.machine_pass)).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 = ('"%s"' % get_string(self.user_pass)).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 = [x if isinstance(x, int) else 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)
Example #15
0
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)
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)
Example #17
0
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
Example #18
0
    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()
Example #19
0
    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)
            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()
Example #20
0
File: getopt.py Project: reqa/samba
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 _ensure_secure_proctitle(self, opt_str, secret_data, data_type="password"):
        """ Make sure no sensitive data (e.g. password) resides in proctitle. """
        import re
        try:
            import setproctitle
        except ModuleNotFoundError:
            msg = ("WARNING: Using %s on command line is insecure. "
                    "Please install the setproctitle python module.\n"
                    % data_type)
            sys.stderr.write(msg)
            sys.stderr.flush()
            return False
        # Regex to search and replace secret data + option with.
        #   .*[ ]+  -> Before the option must be one or more spaces.
        #   [= ]    -> The option and the secret data might be separated by space
        #              or equal sign.
        #   [ ]*.*  -> After the secret data might be one, many or no space.
        pass_opt_re_str = "(.*[ ]+)(%s[= ]%s)([ ]*.*)" % (opt_str, secret_data)
        pass_opt_re = re.compile(pass_opt_re_str)
        # Get current proctitle.
        cur_proctitle = setproctitle.getproctitle()
        # Make sure we build the correct regex.
        if not pass_opt_re.match(cur_proctitle):
            msg = ("Unable to hide %s in proctitle. This is most likely "
                    "a bug!\n" % data_type)
            sys.stderr.write(msg)
            sys.stderr.flush()
            return False
        # String to replace secret data with.
        secret_data_replacer = "xxx"
        # Build string to replace secret data and option with. And as we dont
        # want to change anything else than the secret data within the proctitle
        # we have to check if the option was passed with space or equal sign as
        # separator.
        opt_pass_with_eq = "%s=%s" % (opt_str, secret_data)
        opt_pass_part = re.sub(pass_opt_re_str, r'\2', cur_proctitle)
        if opt_pass_part == opt_pass_with_eq:
            replace_str = "%s=%s" % (opt_str, secret_data_replacer)
        else:
            replace_str = "%s %s" % (opt_str, secret_data_replacer)
        # Build new proctitle:
        new_proctitle = re.sub(pass_opt_re_str,
                            r'\1' + replace_str + r'\3',
                            cur_proctitle)
        # Set new proctitle.
        setproctitle.setproctitle(new_proctitle)

    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._ensure_secure_proctitle(opt_str, arg, "password")
        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
Example #21
0
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)
Example #22
0
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):
        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 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
Example #23
0
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
Example #24
0
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.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 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 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()

        #
        # Some test cases sleep() for self.account_lockout_duration
        # so allow it to be controlled via the subclass
        #
        if not hasattr(self, 'account_lockout_duration'):
            self.account_lockout_duration = 3
        if not hasattr(self, 'lockout_observation_window'):
            self.lockout_observation_window = 3
        self.update_lockout_settings(
            threshold=3,
            duration=self.account_lockout_duration,
            observation_window=self.lockout_observation_window)

        # 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)
Example #26
0
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)