def kerb_cred(monkeypatch): test_kerberos = os.environ.get('PYSPNEGO_TEST_KERBEROS', None) if not test_kerberos or not HAS_K5TEST: pytest.skip("Cannot create Kerberos credential without PYSPNEGO_TEST_REALM being set") realm = K5Realm() try: for k, v in realm.env.items(): monkeypatch.setenv(to_native(k), to_native(v)) yield realm finally: realm.stop() del realm
def test_win_nt_auth_identity_set_domain(): identity = sspi.WinNTAuthIdentity(None, u"original", None) test_domain = u"domain" + to_text(b"\xF0\x9D\x84\x9E") identity.domain = test_domain assert identity.domain == test_domain assert str(identity) == to_native(test_domain) + "\\"
def test_win_nt_auth_identity_set_username(): identity = sspi.WinNTAuthIdentity(u"original", None, None) test_user = u"user" + to_text(b"\xF0\x9D\x84\x9E") identity.username = test_user assert identity.username == test_user assert str(identity) == to_native(test_user)
def test_get_credential_file_env_var_missing_file(tmpdir, monkeypatch): tmp_creds = os.path.join(to_text(tmpdir), u'pÿspᴞӛgӫ TÈ$' '.creds') monkeypatch.setenv('NTLM_USER_FILE', to_native(tmp_creds)) actual = ntlm._get_credential_file() assert actual is None
def ntlm_cred(tmpdir, monkeypatch): cleanup = None try: # Use unicode credentials to test out edge cases when dealing with non-ascii chars. username = u'ÜseӜ' password = u'Pӓ$sw0r̈d' if HAS_SSPI: domain = to_text(socket.gethostname()) # Can only test this out with Windows due to issue with gss-ntlmssp when dealing with surrogate pairs. # https://github.com/gssapi/gss-ntlmssp/issues/20 clef = to_text(b"\xF0\x9D\x84\x9E") username += clef password += clef buff = { 'name': username, 'password': password, 'priv': win32netcon.USER_PRIV_USER, 'comment': 'Test account for pypsnego tests', 'flags': win32netcon.UF_NORMAL_ACCOUNT, } try: win32net.NetUserAdd(None, 1, buff) except win32net.error as err: if err.winerror != 2224: # Account already exists raise def cleanup(): win32net.NetUserDel(None, username) else: domain = u'Dȫm̈Ąiᴞ' # gss-ntlmssp does a string comparison of the user/domain part using the current process locale settings. # To ensure it matches the credentials we specify with the non-ascii chars we need to ensure the locale is # something that can support UTF-8 character comparison. macOS can fail with unknown locale on getlocale(), # just default to env vars if this get fails. try: original_locale = locale.getlocale(locale.LC_CTYPE) except ValueError: original_locale = (None, None) def cleanup(): locale.setlocale(locale.LC_CTYPE, original_locale) locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8') tmp_creds = os.path.join(to_text(tmpdir), u'pÿspᴞӛgӫ TÈ$''.creds') with open(tmp_creds, mode='wb') as fd: fd.write(to_bytes(u'%s:%s:%s' % (domain, username, password))) monkeypatch.setenv('NTLM_USER_FILE', to_native(tmp_creds)) yield u"%s\\%s" % (domain, username), password finally: if cleanup: cleanup()
def test_get_credential_file(tmpdir, monkeypatch): tmp_creds = os.path.join(to_text(tmpdir), u'pÿspᴞӛgӫ TÈ$' '.creds') with open(tmp_creds, mode='wb') as fd: fd.write(b"data") monkeypatch.setenv('NTLM_USER_FILE', to_native(tmp_creds)) actual = ntlm._get_credential_file() assert actual == to_text(tmp_creds)
def test_get_credential_from_file_no_matches(tmpdir, monkeypatch): tmp_creds = os.path.join(to_text(tmpdir), u'pÿspᴞӛgӫ TÈ$' '.creds') monkeypatch.setenv('NTLM_USER_FILE', to_native(tmp_creds)) with open(tmp_creds, mode='wb') as fd: fd.write(b'domain:username:password') with pytest.raises( SpnegoError, match="Failed to find any matching credential in NTLM_USER_FILE " "credential store."): ntlm._NTLMCredential("fake", "username")
def __init__(self, username, password, hostname, service, channel_bindings, context_req, usage, protocol, options, _is_wrapped): # type: (Optional[text_type], Optional[text_type], Optional[text_type], Optional[text_type], Optional[GssChannelBindings], ContextReq, str, text_type, NegotiateOptions, bool) -> None # noqa self.usage = usage.lower() if self.usage not in ['initiate', 'accept']: raise ValueError("Invalid usage '%s', must be initiate or accept" % self.usage) self.protocol = protocol.lower() if self.protocol not in [u'ntlm', u'kerberos', u'negotiate']: raise ValueError( to_native( u"Invalid protocol '%s', must be ntlm, kerberos, or negotiate" % self.protocol)) if self.protocol not in self.available_protocols(options=options): raise ValueError("Protocol %s is not available" % self.protocol) self.username = to_text(username, nonstring='passthru') self.password = to_text(password, nonstring='passthru') self.spn = None if service or hostname: self.spn = to_text( "%s/%s" % (service.upper() if service else "HOST", hostname or "unspecified")) self.channel_bindings = channel_bindings self.options = NegotiateOptions(options) self.context_req = context_req # Generic context requirements. self._context_req = 0 # Provider specific context requirements. for generic, provider in self._context_attr_map: if context_req & generic: self._context_req |= provider self._context_attr = 0 # Provider specific context attributes, set by self.step(). # Whether the context is wrapped inside another context. self._is_wrapped = _is_wrapped # type: bool if options & NegotiateOptions.negotiate_kerberos and ( self.protocol == 'negotiate' and 'kerberos' not in self.available_protocols()): raise FeatureMissingError(NegotiateOptions.negotiate_kerberos) if options & NegotiateOptions.wrapping_iov and not self.iov_available( ): raise FeatureMissingError(NegotiateOptions.wrapping_iov)
def test_get_credential_from_file(line, username, domain, lm_hash, nt_hash, explicit, tmpdir, monkeypatch): tmp_creds = os.path.join(to_text(tmpdir), u'pÿspᴞӛgӫ TÈ$' '.creds') monkeypatch.setenv('NTLM_USER_FILE', to_native(tmp_creds)) with open(tmp_creds, mode='wb') as fd: fd.write(to_bytes(line)) if explicit: actual = ntlm._NTLMCredential(domain, username) else: actual = ntlm._NTLMCredential() assert actual.username == username assert actual.domain == domain assert actual.lm_hash == base64.b16decode(lm_hash) assert actual.nt_hash == base64.b16decode(nt_hash)
else: assert actual == r"SecBuffer(cbBuffer=4, BufferType=1, pvBuffer=b'\x01\x02\x03\x04')" @pytest.mark.skipif( SKIP, reason='Can only test Cython code on Windows with compiled code.') @pytest.mark.parametrize( 'username, domain, expected', [(u"username", u"domain", "domain\\username"), (u"username@DOMAIN", u"", "username@DOMAIN"), (u"username@DOMAIN", None, "username@DOMAIN"), (None, u"domain", "domain\\"), (None, None, u""), (u"", u"", u""), (u"user" + to_text(b"\xF0\x9D\x84\x9E"), u"domain" + to_text(b"\xF0\x9D\x84\x9E"), to_native(u"domain{0}\\user{0}".format(to_text(b"\xF0\x9D\x84\x9E"))))]) def test_win_nt_auth_identity(username, domain, expected): identity = sspi.WinNTAuthIdentity(username, domain, u"password") assert repr( identity) == "<spnego._sspi_raw.sspi.WinNTAuthIdentity %s>" % expected assert str(identity) == expected @pytest.mark.skipif( SKIP, reason='Can only test Cython code on Windows with compiled code.') def test_win_nt_auth_identity_set_username(): identity = sspi.WinNTAuthIdentity(u"original", None, None) test_user = u"user" + to_text(b"\xF0\x9D\x84\x9E") identity.username = test_user
def test_invalid_lm_compat_level(level, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', to_native(level)) expected = "Invalid LM_COMPAT_LEVEL %s, must be between 0 and 5" % level with pytest.raises(SpnegoError, match=re.escape(expected)): ntlm.NTLMProxy("user", "pass")
def negotiated_protocol(self): # FIXME: Try and replicate GSSAPI. Will return None for acceptor until the first token is returned. Negotiate # for both iniator and acceptor until the context is established. package_info = query_context_attributes(self._context, SecPkgAttr.package_info) return to_native(package_info.name).lower()