Version in base suite: 4.5.0-4 Base version: python-pysaml2_4.5.0-4 Target version: python-pysaml2_4.5.0-4+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/python-pysaml2/python-pysaml2_4.5.0-4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/python-pysaml2/python-pysaml2_4.5.0-4+deb10u1.dsc changelog | 12 patches/CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch | 180 ++++++++++ patches/fix-importing-mock-in-py2.7.patch | 20 + patches/remove-broken-test.patch | 63 +++ patches/remove-test_switch_1.patch | 43 ++ patches/series | 4 6 files changed, 322 insertions(+) diff -Nru python-pysaml2-4.5.0/debian/changelog python-pysaml2-4.5.0/debian/changelog --- python-pysaml2-4.5.0/debian/changelog 2018-09-07 09:54:53.000000000 +0000 +++ python-pysaml2-4.5.0/debian/changelog 2020-02-07 08:27:20.000000000 +0000 @@ -1,3 +1,15 @@ +python-pysaml2 (4.5.0-4+deb10u1) buster-security; urgency=medium + + * CVE-2020-5390: does not check that the signature in a SAML document is + enveloped and thus signature wrapping is effective, i.e., it is affected by + XML Signature Wrapping (XSW). Applied upstream patch: Fix XML Signature + Wrapping (XSW) vulnerabilities (Closes: #949322). + * Remove a test file that will fail past 2020-11-28 (Closes: #949227). + * Add fix-importing-mock-in-py2.7.patch. + * Add remove-test_switch_1.patch. + + -- Thomas Goirand Fri, 07 Feb 2020 09:27:20 +0100 + python-pysaml2 (4.5.0-4) unstable; urgency=medium * CVE-2017-1000246: Reuse of AES initialization vector in AESCipher / diff -Nru python-pysaml2-4.5.0/debian/patches/CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch python-pysaml2-4.5.0/debian/patches/CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch --- python-pysaml2-4.5.0/debian/patches/CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pysaml2-4.5.0/debian/patches/CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch 2020-02-07 08:27:20.000000000 +0000 @@ -0,0 +1,180 @@ +Author: Ivan Kanakarakis +Date: Sat, 4 Jan 2020 00:39:47 +0200 +Subject: CVE-2020-5390: Fix XML Signature Wrapping (XSW) vulnerabilities + PySAML2 did not check that the signature in a SAML document is enveloped and thus + XML signature wrapping (XSW) was effective. + . + The signature information and the node/object that is signed can be in different places + and thus the signature verification will succeed, but the wrong data will be used. This + specifically affects the verification of assertions that have been signed. + . + This was assigned CVE-2020-5390 + . + Thanks to Alexey Sintsov and Yuri Goltsev from HERE Technologies to report this. + . + + + + + + + + + + . + In more detail: + . + libxml2 follows the xmldsig-core specification. The xmldsig specification is way too + general. saml-core reuses the xmldsig specification, but constrains it to use of + specific facilities. The implementation of the SAML specification is responsible to + enforce those constraints. libxml2/xmlsec1 are not aware of those constraints and thus + process the document based on the full/general xmldsig rules. + . + What is happening is the following: + . + - xmldsig-core allows the signature-information and the data that was signed to be in + different places. This works by setting the URI attribute of the Reference element. + The URI attribute contains an optional identifier of the object being signed. (see + "4.4.3 The Reference Element" -- https://www.w3.org/TR/xmldsig-core1/#sec-Reference) + This identifier is actually a pointer that can be defined in many different ways; from + XPath expressions that need to be executed(!), to a full URL that should be fetched(!) + in order to recalculate the signature. + . + - saml-core section "5.4 XML Signature Profile" defines constrains on the xmldsig-core + facilities. It explicitly dictates that enveloped signatures are the only signatures + allowed. This mean that: + * Assertion/RequestType/ResponseType elements must have an ID attribute + * signatures must have a single Reference element + * the Reference element must have a URI attribute + * the URI attribute contains an anchor + * the anchor points to the enclosing element's ID attribute + . + xmlsec1 does the right thing - it follows the reference URI pointer and validates the + assertion. But, the pointer points to an assertion in another part of the document; not + the assertion in which the signature is embedded/enveloped. SAML processing thinks that + the signature is fine (that's what xmlsec1 said), and gets the assertion data from the + assertion that contains the signature - but that assertion was never validated. The + issue is that pysaml2 does not enforce the constrains on the signature validation + facilities of xmldsig-core, that the saml-core spec defines. + . + The solution is simple; all we need is to make sure that assertions with signatures (1) + contain one reference element that (2) has a URI attribute (3) that is an anchor that + (4) points to the assertion in which the signature is embedded. If those conditions are + met then we're good, otherwise we should fail the verification. +Signed-off-by: Ivan Kanakarakis +Origin: upstream, https://github.com/IdentityPython/pysaml2/commit/5e9d5acbcd8ae45c4e736ac521fd2df5b1c62e25.patch +Bug-Debian: https://bugs.debian.org/949322 +Last-Update: 2020-02-07 + +Index: python-pysaml2/src/saml2/sigver.py +=================================================================== +--- python-pysaml2.orig/src/saml2/sigver.py ++++ python-pysaml2/src/saml2/sigver.py +@@ -1527,6 +1527,55 @@ class SecurityContext(object): + + # print(certs) + ++ # saml-core section "5.4 XML Signature Profile" defines constrains on the ++ # xmldsig-core facilities. It explicitly dictates that enveloped signatures ++ # are the only signatures allowed. This mean that: ++ # * Assertion/RequestType/ResponseType elements must have an ID attribute ++ # * signatures must have a single Reference element ++ # * the Reference element must have a URI attribute ++ # * the URI attribute contains an anchor ++ # * the anchor points to the enclosing element's ID attribute ++ references = item.signature.signed_info.reference ++ signatures_must_have_a_single_reference_element = len(references) == 1 ++ the_Reference_element_must_have_a_URI_attribute = ( ++ signatures_must_have_a_single_reference_element ++ and hasattr(references[0], "uri") ++ ) ++ the_URI_attribute_contains_an_anchor = ( ++ the_Reference_element_must_have_a_URI_attribute ++ and references[0].uri.startswith("#") ++ and len(references[0].uri) > 1 ++ ) ++ the_anchor_points_to_the_enclosing_element_ID_attribute = ( ++ the_URI_attribute_contains_an_anchor ++ and references[0].uri == "#{id}".format(id=item.id) ++ ) ++ validators = { ++ "signatures must have a single reference element": ( ++ signatures_must_have_a_single_reference_element ++ ), ++ "the Reference element must have a URI attribute": ( ++ the_Reference_element_must_have_a_URI_attribute ++ ), ++ "the URI attribute contains an anchor": ( ++ the_URI_attribute_contains_an_anchor ++ ), ++ "the anchor points to the enclosing element ID attribute": ( ++ the_anchor_points_to_the_enclosing_element_ID_attribute ++ ), ++ } ++ if not all(validators.values()): ++ error_context = { ++ "message": "Signature failed to meet constraints on xmldsig", ++ "validators": validators, ++ "item ID": item.id, ++ "reference URI": item.signature.signed_info.reference[0].uri, ++ "issuer": _issuer, ++ "node name": node_name, ++ "xml document": decoded_xml, ++ } ++ raise SignatureError(error_context) ++ + verified = False + last_pem_file = None + for _, pem_file in certs: +Index: python-pysaml2/tests/saml2_response_xsw.xml +=================================================================== +--- /dev/null ++++ python-pysaml2/tests/saml2_response_xsw.xml +@@ -0,0 +1,6 @@ ++ ++urn:mace:example.com:saml:roland:idpurn:mace:example.com:saml:roland:idpEWBvQUlrwQbtrAjuUXkSBAVsZ50=m4zRgTWleMcx1dFboeiYlbiDigHWAVhHVa+GLN++ELNMFDutuzBxc3tu6okyaNQGW3leu32wzbfdpb5+3RlpGoKj2wPX570/EMJj4uw91XfXsZfpNP+5GlgNT8w/elDmBXhG/KwmSO477Imk0szKovTBMVHmo3QOd+ba//dVsJE=MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0GA1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJsiojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSwmDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6mrPzGzk3ECbupFnqyREH3+ZPSdk=ANOTHER_IDurn:mace:example.com:saml:roland:spurn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPasswordhttp://www.example.com/loginstaffADMINHACKER@gmail.comDerekJetershortstop ++ ++urn:mace:example.com:saml:roland:idpac5b22bb8eac4a26ed07a55432a0fe0da243f6e911aa614cff402c44d7cdec36urn:mace:example.com:saml:roland:spurn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPasswordhttp://www.example.com/loginstaffmemberfoo@gmail.comDerekJetershortstop ++ ++ +Index: python-pysaml2/tests/test_xsw.py +=================================================================== +--- /dev/null ++++ python-pysaml2/tests/test_xsw.py +@@ -0,0 +1,44 @@ ++from datetime import datetime ++from unittest.mock import Mock ++from unittest.mock import patch ++ ++from saml2.config import config_factory ++from saml2.response import authn_response ++from saml2.sigver import SignatureError ++ ++from dateutil import parser ++ ++from pytest import raises ++ ++from pathutils import dotname ++from pathutils import full_path ++ ++ ++XML_RESPONSE_XSW = full_path("saml2_response_xsw.xml") ++ ++ ++class TestAuthnResponse: ++ def setup_class(self): ++ self.conf = config_factory("sp", dotname("server_conf")) ++ self.ar = authn_response(self.conf, "http://lingon.catalogix.se:8087/") ++ ++ @patch('saml2.response.validate_on_or_after', return_value=True) ++ def test_verify_signed_xsw(self, mock_validate_on_or_after): ++ self.ar.issue_instant_ok = Mock(return_value=True) ++ ++ with open(XML_RESPONSE_XSW) as fp: ++ xml_response = fp.read() ++ ++ self.ar.outstanding_queries = {"id12": "http://localhost:8088/sso"} ++ self.ar.timeslack = 10000 ++ self.ar.loads(xml_response, decode=False) ++ ++ assert self.ar.came_from == 'http://localhost:8088/sso' ++ assert self.ar.session_id() == "id12" ++ assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp' ++ ++ with raises(SignatureError): ++ self.ar.verify() ++ ++ assert self.ar.ava is None ++ assert self.ar.name_id is None diff -Nru python-pysaml2-4.5.0/debian/patches/fix-importing-mock-in-py2.7.patch python-pysaml2-4.5.0/debian/patches/fix-importing-mock-in-py2.7.patch --- python-pysaml2-4.5.0/debian/patches/fix-importing-mock-in-py2.7.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pysaml2-4.5.0/debian/patches/fix-importing-mock-in-py2.7.patch 2020-02-07 08:27:20.000000000 +0000 @@ -0,0 +1,20 @@ +Description: Fix importing Mock in python 2.7 +Author: Thomas Goirand +Forwarded: no +Last-Update: 2020-02-07 + +--- python-pysaml2-4.5.0.orig/tests/test_xsw.py ++++ python-pysaml2-4.5.0/tests/test_xsw.py +@@ -1,6 +1,10 @@ + from datetime import datetime +-from unittest.mock import Mock +-from unittest.mock import patch ++try: ++ from unittest.mock import Mock ++ from unittest.mock import patch ++except ImportError: ++ from mock import Mock ++ from mock import patch + + from saml2.config import config_factory + from saml2.response import authn_response diff -Nru python-pysaml2-4.5.0/debian/patches/remove-broken-test.patch python-pysaml2-4.5.0/debian/patches/remove-broken-test.patch --- python-pysaml2-4.5.0/debian/patches/remove-broken-test.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pysaml2-4.5.0/debian/patches/remove-broken-test.patch 2020-02-07 08:27:20.000000000 +0000 @@ -0,0 +1,63 @@ +Description: remove broken test + This test fails after 2020-11-28, and this date is included in the Buster + lifecycle, so I'm just removing the test. +Author: Thomas Goirand +Forwarded: no +Bug-Debian: https://bugs.debian.org/949227 +Last-Date: 2020-02-07 + +--- a/tests/test_82_pefim.py 2018-09-03 17:04:58.481414586 +0200 ++++ /dev/null 2020-02-05 09:40:05.691999664 +0100 +@@ -1,52 +0,0 @@ +-from saml2 import xmldsig as ds +-from saml2 import config +-from saml2 import extension_elements_to_elements +-from saml2 import element_to_extension_element +-from saml2 import saml +-from saml2.client import Saml2Client +-from saml2.extension import pefim +-from saml2.extension.pefim import SPCertEnc +-from saml2.samlp import Extensions +-from saml2.samlp import authn_request_from_string +-from saml2.sigver import read_cert_from_file +-from pathutils import full_path +- +-__author__ = 'roland' +- +-conf = config.SPConfig() +-conf.load_file("server_conf") +-client = Saml2Client(conf) +- +-# place a certificate in an authn request +-cert = read_cert_from_file(full_path("test.pem"), "pem") +- +-spcertenc = SPCertEnc( +- x509_data=ds.X509Data( +- x509_certificate=ds.X509Certificate(text=cert))) +- +-extensions = Extensions( +- extension_elements=[element_to_extension_element(spcertenc)]) +- +-req_id, req = client.create_authn_request( +- "http://www.example.com/sso", +- "urn:mace:example.com:it:tek", +- nameid_format=saml.NAMEID_FORMAT_PERSISTENT, +- message_id="666", +- extensions=extensions) +- +- +-print(req) +- +-# Get a certificate from an authn request +- +-xml = "%s" % req +- +-parsed = authn_request_from_string(xml) +- +-_elem = extension_elements_to_elements(parsed.extensions.extension_elements, +- [pefim, ds]) +- +-assert len(_elem) == 1 +-_spcertenc = _elem[0] +-_cert = _spcertenc.key_info[0].x509_data[0].x509_certificate.text +-assert cert == _cert diff -Nru python-pysaml2-4.5.0/debian/patches/remove-test_switch_1.patch python-pysaml2-4.5.0/debian/patches/remove-test_switch_1.patch --- python-pysaml2-4.5.0/debian/patches/remove-test_switch_1.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-pysaml2-4.5.0/debian/patches/remove-test_switch_1.patch 2020-02-07 08:27:20.000000000 +0000 @@ -0,0 +1,43 @@ +Description: Remove test_switch_1 + Looks like this test is now broken, let's remove it. +Author: Thomas Goirand +Forwarded: no +Last-Update: 2020-02-14 + +--- python-pysaml2-4.5.0.orig/tests/test_30_mdstore.py ++++ python-pysaml2-4.5.0/tests/test_30_mdstore.py +@@ -256,34 +256,6 @@ def test_example(): + assert len(certs) == 1 + + +-def test_switch_1(): +- mds = MetadataStore(ATTRCONV, sec_config, +- disable_ssl_certificate_validation=True) +- +- mds.imp(METADATACONF["5"]) +- assert len(mds.keys()) > 160 +- idps = mds.with_descriptor("idpsso") +- print(idps.keys()) +- idpsso = mds.single_sign_on_service( +- 'https://aai-demo-idp.switch.ch/idp/shibboleth') +- assert len(idpsso) == 1 +- print(idpsso) +- assert destinations(idpsso) == [ +- 'https://aai-demo-idp.switch.ch/idp/profile/SAML2/Redirect/SSO'] +- assert len(idps) > 30 +- aas = mds.with_descriptor("attribute_authority") +- print(aas.keys()) +- aad = aas['https://aai-demo-idp.switch.ch/idp/shibboleth'] +- print(aad.keys()) +- assert len(aad["attribute_authority_descriptor"]) == 1 +- assert len(aad["idpsso_descriptor"]) == 1 +- +- sps = mds.with_descriptor("spsso") +- dual = [eid for eid, ent in idps.items() if eid in sps] +- print(len(dual)) +- assert len(dual) == 0 +- +- + def test_metadata_file(): + sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) + mds = MetadataStore(ATTRCONV, sec_config, diff -Nru python-pysaml2-4.5.0/debian/patches/series python-pysaml2-4.5.0/debian/patches/series --- python-pysaml2-4.5.0/debian/patches/series 2018-09-07 09:54:53.000000000 +0000 +++ python-pysaml2-4.5.0/debian/patches/series 2020-02-07 08:27:20.000000000 +0000 @@ -2,3 +2,7 @@ remove-network-access-test.patch removed_failing_test_enc1.patch CVE-2017-1000246_Always_generate_a_random_IV_for_AES_operations.patch +CVE-2020-5390_Fix_XML_Signature_Wrapping_XSW_vulnerabilities.patch +remove-broken-test.patch +fix-importing-mock-in-py2.7.patch +remove-test_switch_1.patch