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)

Reply via email to