Skip to content

Commit 84e052b

Browse files
michael-ojborean93
authored andcommitted
Use SPNEGO mechanism by default (#41)
Use the correct mechanism as described by * RFC 4559, section 4: The "Negotiate" auth-scheme calls for the use of SPNEGO GSSAPI tokens that the specific mechanism type specifies. * RFC 4178, section 3.2: The GSS-API initiator invokes GSS_Init_sec_context() as normal, but requests that SPNEGO be used. SPNEGO can either be explicitly requested or accepted as the default mechanism. Since both MIT Kerberos and Heimdal use Kerberos 5 as their default mechanism we must explicitly request SPNEGO. Passing raw Kerberos tokens to the acceptor is a violation of these RFCs and some implementations complain about, thus they always need to be wrapped. This closes #41 Signed-off-by: Michael Osipov <[email protected]>
1 parent 7af32f3 commit 84e052b

File tree

5 files changed

+30
-25
lines changed

5 files changed

+30
-25
lines changed

Diff for: HISTORY.rst

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ History
44
FUTURE: TBD
55
-----------
66
- Drop flag for out of sequence detection
7+
- Use SPNEGO mechanism by default
78

89
1.2.3: 2021-02-08
910
-----------------

Diff for: README.rst

+7-7
Original file line numberDiff line numberDiff line change
@@ -194,21 +194,21 @@ applicable). However, an explicit credential can be in instead, if desired.
194194
Explicit Mechanism
195195
------------------
196196

197-
``HTTPSPNEGOAuth`` normally lets the underlying ``gssapi`` library decide which
198-
negotiation mechanism to use. However, an explicit mechanism can be used instead
199-
if desired. The ``mech`` parameter will be passed straight through to ``gssapi``
200-
without interference. It is expected to be an instance of ``gssapi.mechs.Mechanism``.
197+
``HTTPSPNEGOAuth`` normally lets SPNEGO decide which negotiation mechanism to use.
198+
However, an explicit mechanism can be used instead if desired. The ``mech``
199+
parameter will be passed straight through to ``gssapi`` without interference.
200+
It is expected to be an instance of ``gssapi.mechs.Mechanism``.
201201

202202
.. code-block:: python
203203
204204
>>> import gssapi
205205
>>> import requests
206206
>>> from requests_gssapi import HTTPSPNEGOAuth
207207
>>> try:
208-
... spnego = gssapi.mechs.Mechanism.from_sasl_name("SPNEGO")
208+
... krb5 = gssapi.mechs.Mechanism.from_sasl_name("GS2-KRB5")
209209
... except AttributeError:
210-
... spnego = gssapi.OID.from_int_seq("1.3.6.1.5.5.2")
211-
>>> gssapi_auth = HTTPSPNEGOAuth(mech=spnego)
210+
... krb5 = gssapi.OID.from_int_seq("1.2.840.113554.1.2.2")
211+
>>> gssapi_auth = HTTPSPNEGOAuth(mech=krb5)
212212
>>> r = requests.get("http://example.org", auth=gssapi_auth)
213213
...
214214

Diff for: requests_gssapi/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
"""
1515
import logging
1616

17-
from .gssapi_ import HTTPSPNEGOAuth, REQUIRED, OPTIONAL, DISABLED # noqa
17+
from .gssapi_ import HTTPSPNEGOAuth, SPNEGO, REQUIRED, OPTIONAL, DISABLED # noqa
1818
from .exceptions import MutualAuthenticationError
1919
from .compat import NullHandler, HTTPKerberosAuth
2020

2121
logging.getLogger(__name__).addHandler(NullHandler())
2222

2323
__all__ = ('HTTPSPNEGOAuth', 'HTTPKerberosAuth', 'MutualAuthenticationError',
24-
'REQUIRED', 'OPTIONAL', 'DISABLED')
24+
'SPNEGO', 'REQUIRED', 'OPTIONAL', 'DISABLED')
2525
__version__ = '1.2.3'

Diff for: requests_gssapi/gssapi_.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
OPTIONAL = 2
3131
DISABLED = 3
3232

33+
# OID for the SPNEGO mechanism
34+
SPNEGO = gssapi.OID.from_int_seq("1.3.6.1.5.5.2")
35+
3336

3437
class SanitizedResponse(Response):
3538
"""The :class:`Response <Response>` object, which contains a server's
@@ -101,23 +104,23 @@ class HTTPSPNEGOAuth(AuthBase):
101104
Default is `None`.
102105
103106
`mech` is GSSAPI Mechanism (gssapi.Mechanism) to use for negotiation.
104-
Default is `None`
107+
Default is `SPNEGO`
105108
106109
`sanitize_mutual_error_response` controls whether we should clean up
107110
server responses. See the `SanitizedResponse` class.
108111
109112
"""
110113
def __init__(self, mutual_authentication=DISABLED, target_name="HTTP",
111114
delegate=False, opportunistic_auth=False, creds=None,
112-
mech=None, sanitize_mutual_error_response=True):
115+
mech=SPNEGO, sanitize_mutual_error_response=True):
113116
self.context = {}
114117
self.pos = None
115118
self.mutual_authentication = mutual_authentication
116119
self.target_name = target_name
117120
self.delegate = delegate
118121
self.opportunistic_auth = opportunistic_auth
119122
self.creds = creds
120-
self.mech = mech
123+
self.mech = mech if mech else SPNEGO
121124
self.sanitize_mutual_error_response = sanitize_mutual_error_response
122125

123126
def generate_request_header(self, response, host, is_preemptive=False):

Diff for: test_requests_gssapi.py

+14-13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import unittest
1515

1616
from requests_gssapi import REQUIRED
17+
from requests_gssapi import SPNEGO
1718

1819
# Note: we're not using the @mock.patch decorator:
1920
# > My only word of warning is that in the past, the patch decorator hides
@@ -110,7 +111,7 @@ def test_generate_request_header(self):
110111
b64_negotiate_response)
111112
fake_init.assert_called_with(
112113
name=gssapi_sname("[email protected]"),
113-
creds=None, mech=None, flags=gssflags, usage="initiate")
114+
creds=None, mech=SPNEGO, flags=gssflags, usage="initiate")
114115
fake_resp.assert_called_with(b"token")
115116

116117
def test_generate_request_header_init_error(self):
@@ -125,7 +126,7 @@ def test_generate_request_header_init_error(self):
125126
auth.generate_request_header, response, host)
126127
fake_init.assert_called_with(
127128
name=gssapi_sname("[email protected]"),
128-
usage="initiate", flags=gssflags, creds=None, mech=None)
129+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
129130

130131
def test_generate_request_header_step_error(self):
131132
with patch.multiple("gssapi.SecurityContext", __init__=fake_init,
@@ -139,7 +140,7 @@ def test_generate_request_header_step_error(self):
139140
auth.generate_request_header, response, host)
140141
fake_init.assert_called_with(
141142
name=gssapi_sname("[email protected]"),
142-
usage="initiate", flags=gssflags, creds=None, mech=None)
143+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
143144
fail_resp.assert_called_with(b"token")
144145

145146
def test_authenticate_user(self):
@@ -176,7 +177,7 @@ def test_authenticate_user(self):
176177
raw.release_conn.assert_called_with()
177178
fake_init.assert_called_with(
178179
name=gssapi_sname("[email protected]"),
179-
flags=gssflags, usage="initiate", creds=None, mech=None)
180+
flags=gssflags, usage="initiate", creds=None, mech=SPNEGO)
180181
fake_resp.assert_called_with(b"token")
181182

182183
def test_handle_401(self):
@@ -213,7 +214,7 @@ def test_handle_401(self):
213214
raw.release_conn.assert_called_with()
214215
fake_init.assert_called_with(
215216
name=gssapi_sname("[email protected]"),
216-
creds=None, mech=None, flags=gssflags, usage="initiate")
217+
creds=None, mech=SPNEGO, flags=gssflags, usage="initiate")
217218
fake_resp.assert_called_with(b"token")
218219

219220
def test_authenticate_server(self):
@@ -452,7 +453,7 @@ def test_handle_response_401(self):
452453
raw.release_conn.assert_called_with()
453454
fake_init.assert_called_with(
454455
name=gssapi_sname("[email protected]"),
455-
usage="initiate", flags=gssflags, creds=None, mech=None)
456+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
456457
fake_resp.assert_called_with(b"token")
457458

458459
def test_handle_response_401_rejected(self):
@@ -495,7 +496,7 @@ def connection_send(self, *args, **kwargs):
495496
raw.release_conn.assert_called_with()
496497
fake_init.assert_called_with(
497498
name=gssapi_sname("[email protected]"),
498-
usage="initiate", flags=gssflags, creds=None, mech=None)
499+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
499500
fake_resp.assert_called_with(b"token")
500501

501502
def test_generate_request_header_custom_service(self):
@@ -509,7 +510,7 @@ def test_generate_request_header_custom_service(self):
509510
auth.generate_request_header(response, host),
510511
fake_init.assert_called_with(
511512
name=gssapi_sname("[email protected]"),
512-
usage="initiate", flags=gssflags, creds=None, mech=None)
513+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
513514
fake_resp.assert_called_with(b"token")
514515

515516
def test_delegation(self):
@@ -547,7 +548,7 @@ def test_delegation(self):
547548
raw.release_conn.assert_called_with()
548549
fake_init.assert_called_with(
549550
name=gssapi_sname("[email protected]"),
550-
usage="initiate", flags=gssdelegflags, creds=None, mech=None)
551+
usage="initiate", flags=gssdelegflags, creds=None, mech=SPNEGO)
551552
fake_resp.assert_called_with(b"token")
552553

553554
def test_principal_override(self):
@@ -566,7 +567,7 @@ def test_principal_override(self):
566567
fake_init.assert_called_with(
567568
name=gssapi_sname("[email protected]"),
568569
usage="initiate", flags=gssflags,
569-
creds=b"fake creds", mech=None)
570+
creds=b"fake creds", mech=SPNEGO)
570571

571572
def test_realm_override(self):
572573
with patch.multiple("gssapi.SecurityContext", __init__=fake_init,
@@ -580,7 +581,7 @@ def test_realm_override(self):
580581
auth.generate_request_header(response, host)
581582
fake_init.assert_called_with(
582583
name=gssapi_sname("[email protected]"),
583-
usage="initiate", flags=gssflags, creds=None, mech=None)
584+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
584585
fake_resp.assert_called_with(b"token")
585586

586587
def test_opportunistic_auth(self):
@@ -610,7 +611,7 @@ def test_explicit_creds(self):
610611
fake_init.assert_called_with(
611612
name=gssapi_sname("[email protected]"),
612613
usage="initiate", flags=gssflags,
613-
creds=b"fake creds", mech=None)
614+
creds=b"fake creds", mech=SPNEGO)
614615
fake_resp.assert_called_with(b"token")
615616

616617
def test_explicit_mech(self):
@@ -642,7 +643,7 @@ def test_target_name(self):
642643
auth.generate_request_header(response, host)
643644
fake_init.assert_called_with(
644645
name=gssapi_sname("[email protected]"),
645-
usage="initiate", flags=gssflags, creds=None, mech=None)
646+
usage="initiate", flags=gssflags, creds=None, mech=SPNEGO)
646647
fake_resp.assert_called_with(b"token")
647648

648649

0 commit comments

Comments
 (0)