Skip to content

Commit 498da2e

Browse files
Disable mutual authentication by default
Signed-off-by: Robbie Harwood <[email protected]>
1 parent 475fff5 commit 498da2e

File tree

4 files changed

+55
-44
lines changed

4 files changed

+55
-44
lines changed

README.rst

+30-29
Original file line numberDiff line numberDiff line change
@@ -52,57 +52,58 @@ For a more technical explanation of what mutual authentication actually
5252
guarantees, I refer you to rfc2743 (GSSAPIv2), rfc4120 (krb5 in GSSAPI),
5353
rfc4178 (SPNEGO), and rfc4559 (HTTP Negotiate).
5454

55-
REQUIRED
55+
56+
DISABLED
5657
^^^^^^^^
5758

58-
By default, ``HTTPSPNEGOAuth`` will require mutual authentication from the
59-
server, and if a server emits a non-error response which cannot be
60-
authenticated, a ``requests_gssapi.errors.MutualAuthenticationError`` will
61-
be raised. If a server emits an error which cannot be authenticated, it will
62-
be returned to the user but with its contents and headers stripped. If the
63-
response content is more important than the need for mutual auth on errors,
64-
(eg, for certain WinRM calls) the stripping behavior can be suppressed by
65-
setting ``sanitize_mutual_error_response=False``:
59+
By default, there's no need to explicitly disable mutual authentication.
60+
However, for compatability with older versions of request_gssapi or
61+
requests_kerberos, you can explicitly request it not be attempted:
6662

6763
.. code-block:: python
6864
6965
>>> import requests
70-
>>> from requests_gssapi import HTTPSPNEGOAuth, REQUIRED
71-
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=REQUIRED, sanitize_mutual_error_response=False)
72-
>>> r = requests.get("https://windows.example.org/wsman", auth=gssapi_auth)
66+
>>> from requests_gssapi import HTTPSPNEGOAuth, DISABLED
67+
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=DISABLED)
68+
>>> r = requests.get("https://example.org", auth=gssapi_auth)
7369
...
7470
75-
76-
OPTIONAL
71+
REQUIRED
7772
^^^^^^^^
7873

79-
If you'd prefer to not require mutual authentication, you can set your
80-
preference when constructing your ``HTTPSPNEGOAuth`` object:
74+
This was historically the default, but no longer is. If requested,
75+
``HTTPSPNEGOAuth`` will require mutual authentication from the server, and if
76+
a server emits a non-error response which cannot be authenticated, a
77+
``requests_gssapi.errors.MutualAuthenticationError`` will be raised. (See
78+
above for what this means.) If a server emits an error which cannot be
79+
authenticated, it will be returned to the user but with its contents and
80+
headers stripped. If the response content is more important than the need for
81+
mutual auth on errors, (eg, for certain WinRM calls) the stripping behavior
82+
can be suppressed by setting ``sanitize_mutual_error_response=False``:
8183

8284
.. code-block:: python
8385
8486
>>> import requests
85-
>>> from requests_gssapi import HTTPSPNEGOAuth, OPTIONAL
86-
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=OPTIONAL)
87-
>>> r = requests.get("http://example.org", auth=gssapi_auth)
87+
>>> from requests_gssapi import HTTPSPNEGOAuth, REQUIRED
88+
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=REQUIRED, sanitize_mutual_error_response=False)
89+
>>> r = requests.get("https://windows.example.org/wsman", auth=gssapi_auth)
8890
...
8991
90-
This will cause ``requests_gssapi`` to attempt mutual authentication if the
91-
server advertises that it supports it, and cause a failure if authentication
92-
fails, but not if the server does not support it at all.
93-
94-
DISABLED
92+
OPTIONAL
9593
^^^^^^^^
9694

97-
While we don't recommend it, if you'd prefer to never attempt mutual
98-
authentication, you can do that as well:
95+
This will cause ``requests_gssapi`` to attempt mutual authentication if the
96+
server advertises that it supports it, and cause a failure if authentication
97+
fails, but not if the server does not support it at all. This is probably not
98+
what you want: link tampering will either cause hard failures, or silently
99+
cause it to not happen at all. It is retained for compatability.
99100

100101
.. code-block:: python
101102
102103
>>> import requests
103-
>>> from requests_gssapi import HTTPSPNEGOAuth, DISABLED
104-
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=DISABLED)
105-
>>> r = requests.get("http://example.org", auth=gssapi_auth)
104+
>>> from requests_gssapi import HTTPSPNEGOAuth, OPTIONAL
105+
>>> gssapi_auth = HTTPSPNEGOAuth(mutual_authentication=OPTIONAL)
106+
>>> r = requests.get("https://example.org", auth=gssapi_auth)
106107
...
107108
108109
Opportunistic Authentication

requests_gssapi/compat.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import gssapi
77

8-
from .gssapi_ import REQUIRED, HTTPSPNEGOAuth, SPNEGOExchangeError, log
8+
from .gssapi_ import DISABLED, HTTPSPNEGOAuth, SPNEGOExchangeError, log
99

1010
# python 2.7 introduced a NullHandler which we want to use, but to support
1111
# older versions, we implement our own if needed.
@@ -21,7 +21,7 @@ def emit(self, record):
2121

2222
class HTTPKerberosAuth(HTTPSPNEGOAuth):
2323
"""Deprecated compat shim; see HTTPSPNEGOAuth instead."""
24-
def __init__(self, mutual_authentication=REQUIRED, service="HTTP",
24+
def __init__(self, mutual_authentication=DISABLED, service="HTTP",
2525
delegate=False, force_preemptive=False, principal=None,
2626
hostname_override=None, sanitize_mutual_error_response=True):
2727
# put these here for later

requests_gssapi/gssapi_.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ class HTTPSPNEGOAuth(AuthBase):
8484
"""Attaches HTTP GSSAPI Authentication to the given Request object.
8585
8686
`mutual_authentication` controls whether GSSAPI should attempt mutual
87-
authentication. It may be `REQUIRED` (default), `OPTIONAL`, or
88-
`DISABLED`.
87+
authentication. It may be `REQUIRED`, `OPTIONAL`, or `DISABLED`
88+
(default).
8989
9090
`target_name` specifies the remote principal name. It may be either a
9191
GSSAPI name type or a string (default: "HTTP" at the DNS host).
@@ -101,8 +101,9 @@ class HTTPSPNEGOAuth(AuthBase):
101101
102102
`sanitize_mutual_error_response` controls whether we should clean up
103103
server responses. See the `SanitizedResponse` class.
104+
104105
"""
105-
def __init__(self, mutual_authentication=REQUIRED, target_name="HTTP",
106+
def __init__(self, mutual_authentication=DISABLED, target_name="HTTP",
106107
delegate=False, opportunistic_auth=False, creds=None,
107108
sanitize_mutual_error_response=True):
108109
self.context = {}
@@ -123,10 +124,11 @@ def generate_request_header(self, response, host, is_preemptive=False):
123124
124125
"""
125126

126-
gssflags = [gssapi.RequirementFlag.mutual_authentication,
127-
gssapi.RequirementFlag.out_of_sequence_detection]
127+
gssflags = [gssapi.RequirementFlag.out_of_sequence_detection]
128128
if self.delegate:
129129
gssflags.append(gssapi.RequirementFlag.delegate_to_peer)
130+
if self.mutual_authentication != DISABLED:
131+
gssflags.append(gssapi.RequirementFlag.mutual_authentication)
130132

131133
try:
132134
gss_stage = "initiating context"

test_requests_gssapi.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import requests_gssapi
1414
import unittest
1515

16+
from requests_gssapi import REQUIRED
17+
1618
# Note: we're not using the @mock.patch decorator:
1719
# > My only word of warning is that in the past, the patch decorator hides
1820
# > tests when using the standard unittest library.
@@ -26,8 +28,8 @@
2628
# construction, so construct a *really* fake one
2729
fail_resp = Mock(side_effect=gssapi.exceptions.GSSError(0, 0))
2830

29-
gssflags = [gssapi.RequirementFlag.mutual_authentication,
30-
gssapi.RequirementFlag.out_of_sequence_detection]
31+
gssflags = [gssapi.RequirementFlag.out_of_sequence_detection]
32+
mutflags = gssflags + [gssapi.RequirementFlag.mutual_authentication]
3133
gssdelegflags = gssflags + [gssapi.RequirementFlag.delegate_to_peer]
3234

3335
# The base64 behavior we want is that encoding produces a string, but decoding
@@ -237,7 +239,8 @@ def test_handle_other(self):
237239
'www-authenticate': b64_negotiate_server,
238240
'authorization': b64_negotiate_response}
239241

240-
auth = requests_gssapi.HTTPKerberosAuth()
242+
auth = requests_gssapi.HTTPKerberosAuth(
243+
mutual_authentication=REQUIRED)
241244
auth.context = {"www.example.org": gssapi.SecurityContext}
242245

243246
r = auth.handle_other(response_ok)
@@ -255,7 +258,8 @@ def test_handle_response_200(self):
255258
'www-authenticate': b64_negotiate_server,
256259
'authorization': b64_negotiate_response}
257260

258-
auth = requests_gssapi.HTTPKerberosAuth()
261+
auth = requests_gssapi.HTTPKerberosAuth(
262+
mutual_authentication=REQUIRED)
259263
auth.context = {"www.example.org": gssapi.SecurityContext}
260264

261265
r = auth.handle_response(response_ok)
@@ -272,7 +276,8 @@ def test_handle_response_200_mutual_auth_required_failure(self):
272276
response_ok.status_code = 200
273277
response_ok.headers = {}
274278

275-
auth = requests_gssapi.HTTPKerberosAuth()
279+
auth = requests_gssapi.HTTPKerberosAuth(
280+
mutual_authentication=REQUIRED)
276281
auth.context = {"www.example.org": "CTX"}
277282

278283
self.assertRaises(requests_gssapi.MutualAuthenticationError,
@@ -290,7 +295,8 @@ def test_handle_response_200_mutual_auth_required_failure_2(self):
290295
'www-authenticate': b64_negotiate_server,
291296
'authorization': b64_negotiate_response}
292297

293-
auth = requests_gssapi.HTTPKerberosAuth()
298+
auth = requests_gssapi.HTTPKerberosAuth(
299+
mutual_authentication=REQUIRED)
294300
auth.context = {"www.example.org": gssapi.SecurityContext}
295301

296302
self.assertRaises(requests_gssapi.MutualAuthenticationError,
@@ -348,7 +354,8 @@ def test_handle_response_500_mutual_auth_required_failure(self):
348354
response_500.raw = "RAW"
349355
response_500.cookies = "COOKIES"
350356

351-
auth = requests_gssapi.HTTPKerberosAuth()
357+
auth = requests_gssapi.HTTPKerberosAuth(
358+
mutual_authentication=REQUIRED)
352359
auth.context = {"www.example.org": "CTX"}
353360

354361
r = auth.handle_response(response_500)
@@ -524,7 +531,8 @@ def test_delegation(self):
524531
response.connection = connection
525532
response._content = ""
526533
response.raw = raw
527-
auth = requests_gssapi.HTTPKerberosAuth(1, "HTTP", True)
534+
auth = requests_gssapi.HTTPKerberosAuth(service="HTTP",
535+
delegate=True)
528536
r = auth.authenticate_user(response)
529537

530538
self.assertTrue(response in r.history)

0 commit comments

Comments
 (0)