def test_invalid_protocol(): expected = "Invalid protocol specified 'fake', must be kerberos, negotiate, or ntlm" with pytest.raises(ValueError, match=expected): spnego.client(None, None, protocol='fake') with pytest.raises(ValueError, match=expected): spnego.server(protocol='fake')
def main(): server = socket.gethostname() username = '******' password = '******' c = spnego.client(username, password, server) s = spnego.server(server) in_token = None while not c.complete: out_token = c.step(in_token) if not out_token: break in_token = s.step(out_token) print("Client Session key: %s" % base64.b64encode(c.session_key).decode('utf-8')) print("Server Session key: %s" % base64.b64encode(s.session_key).decode('utf-8')) print("Authenticated client: %s" % s.client_principal) c_enc_msg = c.wrap(b"Hello World") s_dec_msg = s.unwrap(c_enc_msg.data) s_enc_msg = s.wrap(s_dec_msg.data) c_dec_msg = c.unwrap(s_enc_msg.data) c_sig = c.sign(b"data") s.verify(b"data", c_sig) s_sig = s.sign(b"data") c.verify(b"data", s_sig)
def test_negotiate_with_raw_ntlm(ntlm_cred): c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_negotiate) negotiate = c.step() assert negotiate.startswith(b"NTLMSSP\x00\x01") assert not c.complete assert not s.complete challenge = s.step(negotiate) assert challenge.startswith(b"NTLMSSP\x00\x02") assert not c.complete assert not s.complete authenticate = c.step(challenge) assert authenticate.startswith(b"NTLMSSP\x00\x03") assert c.complete assert not s.complete final = s.step(authenticate) assert final is None assert c.complete assert s.complete _message_test(c, s)
def test_negotiate_with_kerberos(kerb_cred): c = spnego.client(kerb_cred.user_princ, None, hostname=socket.getfqdn(), options=spnego.NegotiateOptions.use_negotiate) s = spnego.server(options=spnego.NegotiateOptions.use_negotiate) token1 = c.step() assert isinstance(token1, bytes) token2 = s.step(token1) assert isinstance(token2, bytes) token3 = c.step(token2) assert token3 is None # Make sure it reports the right protocol assert c.negotiated_protocol == 'kerberos' assert s.negotiated_protocol == 'kerberos' assert isinstance(c.session_key, bytes) assert isinstance(s.session_key, bytes) assert c.session_key == s.session_key assert c.client_principal is None assert s.client_principal == kerb_cred.user_princ assert c.context_attr & spnego.ContextReq.mutual_auth assert s.context_attr & spnego.ContextReq.mutual_auth _message_test(c, s)
def test_sspi_ntlm_auth_no_sign_or_seal(client_opt, server_opt, ntlm_cred): if client_opt & spnego.NegotiateOptions.use_gssapi or server_opt & spnego.NegotiateOptions.use_gssapi: if 'ntlm' not in spnego.gss.GSSAPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through GSSAPI') elif client_opt & spnego.NegotiateOptions.use_sspi or server_opt & spnego.NegotiateOptions.use_sspi: if 'ntlm' not in spnego.sspi.SSPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through SSPI') # Build the initial context and assert the defaults. c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=client_opt, protocol='ntlm', context_req=0) s = spnego.server(options=server_opt, protocol='ntlm', context_req=0) _ntlm_test(c, s) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] # Client sign, server verify plaintext = os.urandom(3) c_sig = c.sign(plaintext) s.verify(plaintext, c_sig) # Server sign, client verify plaintext = os.urandom(9) s_sig = s.sign(plaintext) c.verify(plaintext, s_sig)
def test_ntlm_bad_mic(ntlm_cred): c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = memoryview(bytearray(c.step(s.step(c.step())))) auth[64:80] = b"\x01" * 16 with pytest.raises(InvalidTokenError, match="Invalid MIC in NTLM authentication message"): s.step(auth.tobytes())
def test_sspi_ntlm_lm_compat(lm_compat_level, ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', str(lm_compat_level)) c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), protocol='ntlm', options=spnego.NegotiateOptions.use_ntlm) s = spnego.server(options=spnego.NegotiateOptions.use_sspi, protocol='ntlm') _ntlm_test(c, s) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] _message_test(c, s)
def test_ntlm_anon_response(ntlm_cred): c = spnego.client(ntlm_cred[0], ntlm_cred[1], options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = Authenticate.unpack(c.step(s.step(c.step()))) anon_auth = Authenticate(flags=auth.flags, lm_challenge_response=b"\x00", nt_challenge_response=b"").pack() with pytest.raises(OperationNotAvailableError, match="Anonymous user authentication not implemented"): s.step(anon_auth)
def test_ntlm_lm_request(ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', '0') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = memoryview(bytearray(c.step(s.step(c.step())))) auth[20:28] = b"\x00" * 8 s.step(auth.tobytes()) assert c.complete assert s.complete
def test_ntlm_no_nt_v1_allowed(ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', '0') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') monkeypatch.setenv('LM_COMPAT_LEVEL', '5') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = c.step(s.step(c.step())) with pytest.raises( InvalidTokenError, match="Acceptor settings are set to reject NTv1 responses"): s.step(auth)
def test_ntlm_nt_v1_request(ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', '0') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') monkeypatch.setenv('LM_COMPAT_LEVEL', '4') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = c.step(s.step(c.step())) s.step(auth) assert c.complete assert s.complete
def test_ntlm_auth(lm_compat_level, ntlm_cred, monkeypatch): if lm_compat_level is not None: monkeypatch.setenv('LM_COMPAT_LEVEL', str(lm_compat_level)) # Build the initial context and assert the defaults. c = spnego.client(ntlm_cred[0], ntlm_cred[1], protocol='ntlm', options=spnego.NegotiateOptions.use_ntlm) s = spnego.server(protocol='ntlm', options=spnego.NegotiateOptions.use_ntlm) _ntlm_test(c, s) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] _message_test(c, s)
def test_ntlm_no_key_exch(ntlm_cred): c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') c._context_req &= ~0x40000000 # NTLMSSP_NEGOTIATE_KEY_EXCH auth = c.step(s.step(c.step())) s.step(auth) # Make sure EncryptedRandomSessionKeyFields was set to 0 (no KEY_EXCH). assert auth[52:54] == b"\x00\x00" plaintext = os.urandom(32) c_wrap_result = c.wrap(plaintext) assert c_wrap_result.encrypted assert c_wrap_result.data != plaintext s_unwrap_result = s.unwrap(c_wrap_result.data) assert s_unwrap_result.data == plaintext assert s_unwrap_result.encrypted plaintext = os.urandom(17) s_wrap_result = s.wrap(plaintext) assert s_wrap_result.encrypted assert s_wrap_result.data != plaintext c_unwrap_result = c.unwrap(s_wrap_result.data) assert c_unwrap_result.data == plaintext assert c_unwrap_result.encrypted plaintext = os.urandom(3) c_sig = c.sign(plaintext) s.verify(plaintext, c_sig) plaintext = os.urandom(9) s_sig = s.sign(plaintext) c.verify(plaintext, s_sig)
def test_ntlm_no_lm_allowed(ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', '0') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') monkeypatch.setenv('LM_COMPAT_LEVEL', '4') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = memoryview(bytearray(c.step(s.step(c.step())))) auth[20:28] = b"\x00" * 8 with pytest.raises( InvalidTokenError, match="Acceptor settings are set to reject LM responses"): s.step(auth)
def test_gssapi_kerberos_auth(kerb_cred): c = spnego.client(kerb_cred.user_princ, None, hostname=socket.getfqdn(), protocol='kerberos', options=spnego.NegotiateOptions.use_gssapi) s = spnego.server(options=spnego.NegotiateOptions.use_gssapi, protocol='kerberos') assert not c.complete assert not s.complete assert s.negotiated_protocol is None with pytest.raises(SpnegoError, match="Retrieving session key"): _ = c.session_key with pytest.raises(SpnegoError, match="Retrieving session key"): _ = s.session_key token1 = c.step() assert isinstance(token1, bytes) assert not c.complete assert not s.complete assert s.negotiated_protocol is None token2 = s.step(token1) assert isinstance(token2, bytes) assert not c.complete assert s.complete assert s.negotiated_protocol == 'kerberos' token3 = c.step(token2) assert token3 is None assert c.complete assert s.complete assert isinstance(c.session_key, bytes) assert isinstance(s.session_key, bytes) assert c.session_key == s.session_key assert c.client_principal is None assert s.client_principal == kerb_cred.user_princ _message_test(c, s)
def test_gssapi_ntlm_lm_compat(lm_compat_level, ntlm_cred, monkeypatch): monkeypatch.setenv('LM_COMPAT_LEVEL', str(lm_compat_level)) c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), protocol='ntlm', options=spnego.NegotiateOptions.use_ntlm) s = spnego.server(options=spnego.NegotiateOptions.use_gssapi, protocol='ntlm') # gss-ntlmssp version on CI may be too old to test the session key test_session_key = 'ntlm' in spnego.gss.GSSAPIProxy.available_protocols( spnego.NegotiateOptions.session_key) _ntlm_test(c, s, test_session_key=test_session_key) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] _message_test(c, s)
def test_token_acceptor_first(ntlm_cred): c = spnego.client(ntlm_cred[0], ntlm_cred[1], options=spnego.NegotiateOptions.use_negotiate) s = spnego.server(options=spnego.NegotiateOptions.use_negotiate) assert c._mech_list == [] assert s._mech_list == [] token1 = s.step() assert isinstance(token1, bytes) assert not c.complete assert not s.complete assert c._mech_list == [] assert s._mech_list == [GSSMech.ntlm.value] negotiate = c.step(token1) assert isinstance(negotiate, bytes) assert not c.complete assert not s.complete assert c._mech_list == [GSSMech.ntlm.value] assert s._mech_list == [GSSMech.ntlm.value] challenge = s.step(negotiate) assert isinstance(challenge, bytes) assert not c.complete assert not s.complete authenticate = c.step(challenge) assert isinstance(authenticate, bytes) assert not c.complete assert not s.complete mech_list_mic = s.step(authenticate) assert isinstance(mech_list_mic, bytes) assert not c.complete assert s.complete final_token = c.step(mech_list_mic) assert final_token is None assert c.complete assert s.complete
def test_ntlm_invalid_password(client_opt, ntlm_cred): if client_opt & spnego.NegotiateOptions.use_gssapi: if 'ntlm' not in spnego.gss.GSSAPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through GSSAPI') elif client_opt & spnego.NegotiateOptions.use_sspi: if 'ntlm' not in spnego.sspi.SSPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through SSPI') c = spnego.client(ntlm_cred[0], u"Invalid", hostname=socket.gethostname(), options=client_opt, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') auth = c.step(s.step(c.step())) with pytest.raises(InvalidTokenError, match="Invalid NTLM response from initiator"): s.step(auth)
def test_sspi_ntlm_auth(client_opt, server_opt, cbt, ntlm_cred): # Build the initial context and assert the defaults. kwargs = { 'protocol': 'ntlm', } if cbt: kwargs[ 'channel_bindings'] = spnego.channel_bindings.GssChannelBindings( application_data=b'test_data:\x00\x01') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=client_opt, **kwargs) s = spnego.server(options=server_opt, **kwargs) _ntlm_test(c, s) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] _message_test(c, s)
def test_gssapi_ntlm_auth(client_opt, server_opt, ntlm_cred, cbt): # Build the initial context and assert the defaults. kwargs = { 'protocol': 'ntlm', } if cbt: kwargs[ 'channel_bindings'] = spnego.channel_bindings.GssChannelBindings( application_data=b'test_data:\x00\x01') c = spnego.client(ntlm_cred[0], ntlm_cred[1], options=client_opt, **kwargs) s = spnego.server(options=server_opt, **kwargs) # gss-ntlmssp version on CI may be too old to test the session key test_session_key = 'ntlm' in spnego.gss.GSSAPIProxy.available_protocols( spnego.NegotiateOptions.session_key) _ntlm_test(c, s, test_session_key=test_session_key) assert c.client_principal is None assert s.client_principal == ntlm_cred[0] _message_test(c, s)
def test_ntlm_bad_bindings(client_opt, present, ntlm_cred): if client_opt & spnego.NegotiateOptions.use_gssapi: if 'ntlm' not in spnego.gss.GSSAPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through GSSAPI') elif client_opt & spnego.NegotiateOptions.use_sspi: if 'ntlm' not in spnego.sspi.SSPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through SSPI') initiator_cbt = None if present: initiator_cbt = spnego.channel_bindings.GssChannelBindings( application_data=b"tls-host-data:bad") c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=client_opt, protocol='ntlm', channel_bindings=initiator_cbt) acceptor_cbt = spnego.channel_bindings.GssChannelBindings( application_data=b"tls-host-data:test") s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm', channel_bindings=acceptor_cbt) auth = c.step(s.step(c.step())) if present: expected = "Acceptor bindings do not match initiator bindings" else: expected = "Acceptor bindings specified but not present in initiator response" with pytest.raises(BadBindingsError, match=expected): s.step(auth)
def test_ntlm_verify_fail(client_opt, ntlm_cred): if client_opt & spnego.NegotiateOptions.use_gssapi: if 'ntlm' not in spnego.gss.GSSAPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through GSSAPI') elif client_opt & spnego.NegotiateOptions.use_sspi: if 'ntlm' not in spnego.sspi.SSPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through SSPI') c = spnego.client(ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(), options=client_opt, protocol='ntlm') s = spnego.server(options=spnego.NegotiateOptions.use_ntlm, protocol='ntlm') s.step(c.step(s.step(c.step()))) c.sign(b"data") sig = c.sign(b"data 2") with pytest.raises(BadMICError, match="Invalid Message integrity Check"): s.verify(b"data", sig)
def test_negotiate_through_python_ntlm(client_opt, server_opt, ntlm_cred, monkeypatch): if client_opt & spnego.NegotiateOptions.use_negotiate and server_opt & spnego.NegotiateOptions.use_negotiate: # Make sure we pretend that the system libraries aren't available def available_protocols(*args, **kwargs): return [] monkeypatch.setattr(spnego.gss, '_available_protocols', available_protocols) monkeypatch.setattr(spnego.sspi, '_available_protocols', available_protocols) elif client_opt & spnego.NegotiateOptions.use_gssapi or server_opt & spnego.NegotiateOptions.use_gssapi: if 'ntlm' not in spnego.gss.GSSAPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through GSSAPI') elif client_opt & spnego.NegotiateOptions.use_sspi or server_opt & spnego.NegotiateOptions.use_sspi: if 'ntlm' not in spnego.sspi.SSPIProxy.available_protocols(): pytest.skip('Test requires NTLM to be available through SSPI') # Build the initial context and assert the defaults. c = spnego.client(ntlm_cred[0], ntlm_cred[1], protocol='negotiate', options=client_opt, context_req=spnego.ContextReq.delegate | spnego.ContextReq.default) s = spnego.server(protocol='negotiate', options=server_opt) assert not c.complete assert not s.complete negotiate = c.step() assert isinstance(negotiate, bytes) assert not c.complete assert not s.complete challenge = s.step(negotiate) assert isinstance(challenge, bytes) assert not c.complete assert not s.complete authenticate = c.step(challenge) assert isinstance(authenticate, bytes) assert not c.complete assert not s.complete mech_list_mic = s.step(authenticate) assert isinstance(mech_list_mic, bytes) assert not c.complete assert s.complete assert c.client_principal is None assert s.client_principal == ntlm_cred[0] mech_list_resp = c.step(mech_list_mic) assert mech_list_resp is None assert c.complete assert s.complete assert c.negotiated_protocol == 'ntlm' assert s.negotiated_protocol == 'ntlm' _message_test(c, s)