Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pyspnego for openSUSE:Factory
checked in at 2022-10-03 13:44:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pyspnego (Old)
and /work/SRC/openSUSE:Factory/.python-pyspnego.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyspnego"
Mon Oct 3 13:44:27 2022 rev:10 rq:1007433 version:0.5.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pyspnego/python-pyspnego.changes
2022-02-23 16:26:08.915508183 +0100
+++
/work/SRC/openSUSE:Factory/.python-pyspnego.new.2275/python-pyspnego.changes
2022-10-03 13:44:43.389344300 +0200
@@ -1,0 +2,14 @@
+Sat Oct 1 12:14:25 UTC 2022 - Dirk M??ller <[email protected]>
+
+- update to 0.5.0:
+ * Added the `auth_stage` extra_info for a CredSSP context to give a human
+ friendly indication of what sub auth stage it is up to.
+ * Added the `protocol_version` extra_info for a CredSSP context to return the
+ negotiated CredSSP protocol version.
+ * Added the `credssp_min_protocol` keyword argument for a CredSSP context to
+ set a minimum version the caller will accept of the peer.
+ * This can be set to `5+` to ensure the peer supports and applies the
mitigations for CVE-2018-0886.
+ * Added safeguards when trying to retrieve the completed context attributes
+ of `NegotiateProxy` before any contexts have been set up
+
+-------------------------------------------------------------------
Old:
----
pyspnego-0.4.0.tar.gz
New:
----
pyspnego-0.5.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pyspnego.spec ++++++
--- /var/tmp/diff_new_pack.udBIXj/_old 2022-10-03 13:44:44.501346747 +0200
+++ /var/tmp/diff_new_pack.udBIXj/_new 2022-10-03 13:44:44.505346756 +0200
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-pyspnego
-Version: 0.4.0
+Version: 0.5.0
Release: 0
Summary: Python SPNEGO authentication library
License: MIT
++++++ pyspnego-0.4.0.tar.gz -> pyspnego-0.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/CHANGELOG.md
new/pyspnego-0.5.0/CHANGELOG.md
--- old/pyspnego-0.4.0/CHANGELOG.md 2022-02-16 08:45:22.000000000 +0100
+++ new/pyspnego-0.5.0/CHANGELOG.md 2022-02-21 01:05:25.000000000 +0100
@@ -1,5 +1,14 @@
# Changelog
+## 0.5.0 - 2022-02-21
+
+* Added the `auth_stage` extra_info for a CredSSP context to give a human
friendly indication of what sub auth stage it is up to.
+* Added the `protocol_version` extra_info for a CredSSP context to return the
negotiated CredSSP protocol version.
+* Added the `credssp_min_protocol` keyword argument for a CredSSP context to
set a minimum version the caller will accept of the peer.
+ * This can be set to `5+` to ensure the peer supports and applies the
mitigations for CVE-2018-0886.
+* Added safeguards when trying to retrieve the completed context attributes of
`NegotiateProxy` before any contexts have been set up
(https://github.com/jborean93/pyspnego/issues/33)
+
+
## 0.4.0 - 2022-02-16
### Features
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/src/spnego/_context.py
new/pyspnego-0.5.0/src/spnego/_context.py
--- old/pyspnego-0.4.0/src/spnego/_context.py 2022-02-16 08:45:22.000000000
+0100
+++ new/pyspnego-0.5.0/src/spnego/_context.py 2022-02-21 01:05:25.000000000
+0100
@@ -672,6 +672,18 @@
The :class:`ssl.SSLObject` instance used for the CredSSP
context.
+ auth_stage - added in 0.5.0:
+ A string representing that sub authentication stage being
+ performed in the CredSSP authentication stepping. The value
+ here is meant to be a human friendly representation and not
+ something to be relied upon.
+
+ protocol_version - added in 0.5.0:
+ The CredSSP protocol version that was negotiated between the
+ initiator and acceptor. This is the minimum version number
+ offered by both parties once the Negotiate authentication stage
+ is complete.
+
Args:
name: The name/id of the information to retrieve.
default: The default value to return if the information is not
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/src/spnego/_credssp.py
new/pyspnego-0.5.0/src/spnego/_credssp.py
--- old/pyspnego-0.4.0/src/spnego/_credssp.py 2022-02-16 08:45:22.000000000
+0100
+++ new/pyspnego-0.5.0/src/spnego/_credssp.py 2022-02-21 01:05:25.000000000
+0100
@@ -212,6 +212,28 @@
The acceptor logic is mostly done as a proof of concept and for use with
testing. Use at your own risk.
+ Currently this context proxy supports the following CredSSP protocols:
+
+ 2:
+ Earliest protocol that ships with Windows XP, Vista, 7, 8, Server
+ 2008, 2008 R2, and 2012. This version is susceptible to
+ CVE-2018-0886
+ 3:
+ Introduced with Windows 8.1 and Server 2012 R2. Adds the errorCode
+ field for better error details on a failure. This version is
+ susceptible to CVE-2018-0886.
+ 4:
+ Largely the same as 3, introduced in an older Win 10 build.
+ 5:
+ Same as version 2 but with the mitigations for CVE-2018-0886
+ applied.
+ 6:
+ Same as version 3 and 4 but with the mitigations for CVE-2018-0886
+ applied.
+
+ If the context proxy is talking to a peer with a higher protocol number it
+ is treated the same as what the client supports.
+
Optional kwargs supports by CredSSPProxy:
credssp_negotiate_context: Use this contest for the underlying
@@ -222,6 +244,13 @@
credssp_tls_context: Custom :class:`CredSSPTLSContext` to use for the
CredSSP exchange. See `spnego.tls` for helper methods to generate
a custom TLS context.
+
+ credssp_min_protocol: The minimum CredSSP authentication protocol the
+ context will allow. This can control whether the server rejects
+ peers running on a lower protocol version than what is set to
+ enforce a better security baseline. The default is 2 which works
+ against all CredSSP peers. To ensure the mitigations for
+ CVE-2018-0886 is in place set this value to 5 or higher.
"""
def __init__(
@@ -256,6 +285,9 @@
self._ts_credential: typing.Optional[TSCredentials] = None
self._complete = False
self._step_gen: typing.Optional[typing.Generator[bytes,
typing.Optional[bytes], None]] = None
+ self._auth_stage = "TLS Handshake"
+ self._min_version: int = kwargs.get("credssp_min_protocol", 2)
+ self._selected_version: typing.Optional[int] = None
self._tls_context: CredSSPTLSContext
if "credssp_tls_context" in kwargs:
@@ -356,12 +388,17 @@
if self._auth_context.complete or (out_token and
b"NTLMSSP\x00\x03\x00\x00\x00" in out_token):
break
- # TODO: Check that version meets minimum requirement
- version = min(auth_response.version, _CREDSSP_VERSION)
- log.debug("Negotiated CredSSP version: %d" % version)
+ self._selected_version = min(auth_response.version, _CREDSSP_VERSION)
+ log.debug("Negotiated CredSSP version: %d" % self._selected_version)
+ if self._selected_version < self._min_version:
+ msg = (
+ f"The peer protocol version was {auth_response.version} and
did not meet the minimum "
+ f"requirements of {self._min_version}"
+ )
+ raise InvalidTokenError(context_msg=msg)
pub_key_nego_token = NegoData(out_token) if out_token else None
- nonce = os.urandom(32) if version > 4 else None
+ nonce = os.urandom(32) if self._selected_version > 4 else None
pub_value = _get_pub_key_auth(public_key, "initiate", nonce=nonce)
pub_key_request = TSRequest(
version=_CREDSSP_VERSION,
@@ -398,11 +435,17 @@
"""The acceptor authentication steps of CredSSP."""
in_token = yield from self._step_tls(in_token)
- # The version to use as the acceptor should be the smaller of the
client and _CREDSSP_VERSION.
- # TODO: Add check that sets minimum client version supported.
auth_request = TSRequest.unpack(in_token or b"")
- version = min(auth_request.version, _CREDSSP_VERSION)
- log.debug("Negotiated CredSSP version: %d" % version)
+
+ # The version to use as the acceptor should be the smaller of the
client and _CREDSSP_VERSION.
+ self._selected_version = min(auth_request.version, _CREDSSP_VERSION)
+ log.debug("Negotiated CredSSP version: %d" % self._selected_version)
+ if self._selected_version < self._min_version:
+ msg = (
+ f"The peer protocol version was {auth_request.version} and did
not meet the minimum "
+ f"requirements of {self._min_version}"
+ )
+ raise InvalidTokenError(context_msg=msg)
try:
log.debug("Starting CredSSP authentication phase")
@@ -431,7 +474,7 @@
except SpnegoError as e:
# Version 2 and 5 don't support the errorCode field and the
initiator won't expect a token back.
log.warning("Received CredSSP error when accepting credentials:
%s", e)
- if version in [3, 4] or version >= 6:
+ if self._selected_version in [3, 4] or self._selected_version >= 6:
error_token = TSRequest(_CREDSSP_VERSION,
error_code=e.nt_status)
yield from self._yield_ts_request(error_token, "Authentication
failure")
@@ -450,6 +493,7 @@
pub_key_response = TSRequest(_CREDSSP_VERSION, nego_tokens=nego_token,
pub_key_auth=server_key)
auth_request = yield from self._yield_ts_request(pub_key_response,
"Public key exchange")
+ self._auth_stage = "Credential exchange"
if not auth_request.auth_info:
raise InvalidTokenError(context_msg="No credential received on
CredSSP TSRequest from initiator")
@@ -496,6 +540,8 @@
context_msg: str,
) -> typing.Generator[bytes, bytes, TSRequest]:
"""Exchanges a TSRequest between the initiator and acceptor."""
+ self._auth_stage = context_msg
+
out_request = ts_request.pack()
log.debug("CredSSP TSRequest output: %s" %
to_text(base64.b64encode(out_request)))
wrapped_response = yield self.wrap(out_request).data
@@ -525,6 +571,12 @@
elif name == "ssl_object":
return self._tls_object
+ elif name == "auth_stage":
+ return self._auth_stage
+
+ elif name == "protocol_version" and self._selected_version is not None:
+ return self._selected_version
+
else:
return default
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/src/spnego/_negotiate.py
new/pyspnego-0.5.0/src/spnego/_negotiate.py
--- old/pyspnego-0.4.0/src/spnego/_negotiate.py 2022-02-16 08:45:22.000000000
+0100
+++ new/pyspnego-0.5.0/src/spnego/_negotiate.py 2022-02-21 01:05:25.000000000
+0100
@@ -99,7 +99,7 @@
@property
def client_principal(self) -> typing.Optional[str]:
- return self._context.client_principal
+ return self._context.client_principal if self._context_list else None
@property
def complete(self) -> bool:
@@ -107,15 +107,15 @@
@property
def context_attr(self) -> ContextReq:
- return self._context.context_attr
+ return self._context.context_attr if self._context_list else
ContextReq.none
@property
def negotiated_protocol(self) -> typing.Optional[str]:
- return self._context.negotiated_protocol
+ return self._context.negotiated_protocol if self._context_list else
None
@property
def session_key(self) -> bytes:
- return self._context.session_key
+ return self._context.session_key if self._context_list else b""
def step(self, in_token: typing.Optional[bytes] = None) ->
typing.Optional[bytes]:
log.debug("SPNEGO step input: %s", base64.b64encode(in_token or
b"").decode())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/src/spnego/_version.py
new/pyspnego-0.5.0/src/spnego/_version.py
--- old/pyspnego-0.4.0/src/spnego/_version.py 2022-02-16 08:45:22.000000000
+0100
+++ new/pyspnego-0.5.0/src/spnego/_version.py 2022-02-21 01:05:25.000000000
+0100
@@ -1,4 +1,4 @@
# Copyright: (c) 2020, Jordan Borean (@jborean93) <[email protected]>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
-__version__ = "0.4.0"
+__version__ = "0.5.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pyspnego-0.4.0/tests/test_auth.py
new/pyspnego-0.5.0/tests/test_auth.py
--- old/pyspnego-0.4.0/tests/test_auth.py 2022-02-16 08:45:22.000000000
+0100
+++ new/pyspnego-0.5.0/tests/test_auth.py 2022-02-21 01:05:25.000000000
+0100
@@ -26,7 +26,12 @@
from spnego._credssp_structures import TSPasswordCreds
from spnego._ntlm_raw.crypto import lmowfv1, ntowfv1
from spnego._spnego import NegTokenResp, unpack_token
-from spnego.exceptions import InvalidCredentialError, NoCredentialError,
SpnegoError
+from spnego.exceptions import (
+ InvalidCredentialError,
+ InvalidTokenError,
+ NoCredentialError,
+ SpnegoError,
+)
def _message_test(client: spnego.ContextProxy, server: spnego.ContextProxy) ->
None:
@@ -1018,6 +1023,8 @@
if version:
monkeypatch.setattr(spnego._credssp, "_CREDSSP_VERSION", version)
+ else:
+ version = spnego._credssp._CREDSSP_VERSION
c = spnego.client(ntlm_cred[0], ntlm_cred[1],
hostname=socket.gethostname(), protocol="credssp", options=options)
s = spnego.server(protocol="credssp", options=options, **context_kwargs)
@@ -1027,10 +1034,12 @@
assert c.get_extra_info("client_credential") is None
assert isinstance(c.get_extra_info("sslcontext"), ssl.SSLContext)
assert isinstance(c.get_extra_info("ssl_object"), ssl.SSLObject)
+ assert c.get_extra_info("auth_stage") == "TLS Handshake"
assert s.get_extra_info("client_credential") is None
assert isinstance(s.get_extra_info("sslcontext"), ssl.SSLContext)
assert isinstance(s.get_extra_info("ssl_object"), ssl.SSLObject)
+ assert s.get_extra_info("auth_stage") == "TLS Handshake"
assert c.client_principal is None
assert c.get_extra_info("client_credential") is None
@@ -1049,18 +1058,26 @@
assert not s.complete
ntlm3_pub_key = c.step(server_tls_token)
+ assert c.get_extra_info("auth_stage") == "Public key exchange"
+ assert s.get_extra_info("auth_stage").startswith("Authentication")
assert not c.complete
assert not s.complete
server_pub_key = s.step(ntlm3_pub_key)
+ assert c.get_extra_info("auth_stage") == "Public key exchange"
+ assert s.get_extra_info("auth_stage") == "Public key exchange"
assert not c.complete
assert not s.complete
credential = c.step(server_pub_key)
+ assert c.get_extra_info("auth_stage") == "Credential exchange"
+ assert s.get_extra_info("auth_stage") == "Public key exchange"
assert c.complete
assert not s.complete
final_token = s.step(credential)
+ assert c.get_extra_info("auth_stage") == "Credential exchange"
+ assert s.get_extra_info("auth_stage") == "Credential exchange"
assert final_token is None
assert c.complete
assert s.complete
@@ -1077,6 +1094,9 @@
assert client_credential.domain_name == domain
assert client_credential.password == ntlm_cred[1]
+ assert c.get_extra_info("protocol_version") == version
+ assert s.get_extra_info("protocol_version") == version
+
_message_test(c, s)
plaintext = os.urandom(16)
@@ -1251,3 +1271,56 @@
assert client_credential.password == "password"
_message_test(c, s)
+
+
+def test_credssp_min_protocol_failure_initiator(ntlm_cred, monkeypatch):
+ monkeypatch.setattr(spnego._credssp, "_CREDSSP_VERSION", 4)
+
+ c = spnego.client(
+ ntlm_cred[0], ntlm_cred[1], hostname=socket.gethostname(),
protocol="credssp", credssp_min_protocol=5
+ )
+ s = spnego.server(protocol="credssp")
+
+ # The TLS handshake can differ based on the protocol selected, keep on
looping until we see the auth_context set up
+ # For NTLM the auth context will be present after the first exchange of
NTLM tokens.
+ server_tls_token = None
+ while c._auth_context is None: # type: ignore[attr-defined]
+ client_tls_token = c.step(server_tls_token)
+ assert not c.complete
+ assert not s.complete
+
+ server_tls_token = s.step(client_tls_token)
+ assert not c.complete
+ assert not s.complete
+
+ with pytest.raises(
+ InvalidTokenError, match="The peer protocol version was 4 and did not
meet the minimum requirements of 5"
+ ):
+ c.step(server_tls_token)
+
+
+def test_credssp_min_protocol_failure_acceptor(ntlm_cred, monkeypatch):
+ monkeypatch.setattr(spnego._credssp, "_CREDSSP_VERSION", 4)
+
+ c = spnego.client(ntlm_cred[0], ntlm_cred[1],
hostname=socket.gethostname(), protocol="credssp")
+ s = spnego.server(protocol="credssp", credssp_min_protocol=5)
+
+ # The TLS handshake can differ based on the protocol selected, keep on
looping until we see the auth_context set up
+ # For NTLM the auth context will be present after the first exchange of
NTLM tokens.
+ server_tls_token = None
+ while True:
+ client_tls_token = c.step(server_tls_token)
+ assert not c.complete
+ assert not s.complete
+
+ if c.get_extra_info("auth_stage").startswith("Authentication"):
+ break
+
+ server_tls_token = s.step(client_tls_token)
+ assert not c.complete
+ assert not s.complete
+
+ with pytest.raises(
+ InvalidTokenError, match="The peer protocol version was 4 and did not
meet the minimum requirements of 5"
+ ):
+ s.step(client_tls_token)