|
|
This fixes XXE issues on anything where pysaml2 parses XML directly as part of |
|
This fixes XXE issues on anything where pysaml2 parses XML directly as part of |
1 |
issue #366. It doesn't address the xmlsec issues discussed on that ticket as |
1 |
issue #366. It doesn't address the xmlsec issues discussed on that ticket as |
2 |
they are out of reach of a direct fix and need the underlying library to fix |
2 |
they are out of reach of a direct fix and need the underlying library to fix |
3 |
this issue. |
3 |
this issue. |
4 |
. |
4 |
. |
5 |
The patch has been backported form the 3.0 branch to 2.0 by zigo@debian.org. |
5 |
The patch has been backported form the 3.0 branch to 2.0 by zigo@debian.org. |
6 |
-- python-pysaml2-2.0.0.orig/setup.py |
6 |
++ python-pysaml2-2.0.0/setup.py |
|
46 |
'pycrypto', # 'Crypto' |
46 |
'pycrypto', # 'Crypto' |
47 |
'pytz', |
47 |
'pytz', |
48 |
'pyOpenSSL', |
48 |
'pyOpenSSL', |
49 |
'python-dateutil' |
49 |
'python-dateutil', |
|
|
50 |
'defusedxml' |
50 |
] |
51 |
] |
51 |
|
52 |
|
52 |
tests_require = [ |
53 |
tests_require = [ |
53 |
-- python-pysaml2-2.0.0.orig/src/saml2/__init__.py |
54 |
++ python-pysaml2-2.0.0/src/saml2/__init__.py |
|
33 |
import cElementTree as ElementTree |
33 |
import cElementTree as ElementTree |
34 |
except ImportError: |
34 |
except ImportError: |
35 |
from elementtree import ElementTree |
35 |
from elementtree import ElementTree |
|
|
36 |
import defusedxml.ElementTree |
36 |
|
37 |
|
37 |
root_logger = logging.getLogger(__name__) |
38 |
root_logger = logging.getLogger(__name__) |
38 |
root_logger.level = logging.NOTSET |
39 |
root_logger.level = logging.NOTSET |
Lines 82-88
def create_class_from_xml_string(target_
|
Link Here
|
---|
|
82 |
the contents of the XML - or None if the root XML tag and namespace did |
83 |
the contents of the XML - or None if the root XML tag and namespace did |
83 |
not match those of the target class. |
84 |
not match those of the target class. |
84 |
""" |
85 |
""" |
85 |
tree = ElementTree.fromstring(xml_string) |
86 |
tree = defusedxml.ElementTree.fromstring(xml_string) |
86 |
return create_class_from_element_tree(target_class, tree) |
87 |
return create_class_from_element_tree(target_class, tree) |
87 |
|
88 |
|
88 |
|
89 |
|
Lines 264-270
class ExtensionElement(object):
|
Link Here
|
---|
|
264 |
|
265 |
|
265 |
|
266 |
|
266 |
def extension_element_from_string(xml_string): |
267 |
def extension_element_from_string(xml_string): |
267 |
element_tree = ElementTree.fromstring(xml_string) |
268 |
element_tree = defusedxml.ElementTree.fromstring(xml_string) |
268 |
return _extension_element_from_element_tree(element_tree) |
269 |
return _extension_element_from_element_tree(element_tree) |
269 |
|
270 |
|
270 |
|
271 |
|
271 |
-- python-pysaml2-2.0.0.orig/src/saml2/pack.py |
272 |
++ python-pysaml2-2.0.0/src/saml2/pack.py |
|
48 |
import cElementTree as ElementTree |
48 |
import cElementTree as ElementTree |
49 |
except ImportError: |
49 |
except ImportError: |
50 |
from elementtree import ElementTree |
50 |
from elementtree import ElementTree |
|
|
51 |
import defusedxml.ElementTree |
51 |
|
52 |
|
52 |
NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" |
53 |
NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" |
53 |
FORM_SPEC = """<form method="post" action="%s"> |
54 |
FORM_SPEC = """<form method="post" action="%s"> |
Lines 218-224
def parse_soap_enveloped_saml(text, body
|
Link Here
|
---|
|
218 |
:param text: The SOAP object as XML |
219 |
:param text: The SOAP object as XML |
219 |
:return: header parts and body as saml.samlbase instances |
220 |
:return: header parts and body as saml.samlbase instances |
220 |
""" |
221 |
""" |
221 |
envelope = ElementTree.fromstring(text) |
222 |
envelope = defusedxml.ElementTree.fromstring(text) |
222 |
assert envelope.tag == '{%s}Envelope' % NAMESPACE |
223 |
assert envelope.tag == '{%s}Envelope' % NAMESPACE |
223 |
|
224 |
|
224 |
#print len(envelope) |
225 |
#print len(envelope) |
225 |
-- python-pysaml2-2.0.0.orig/src/saml2/soap.py |
226 |
++ python-pysaml2-2.0.0/src/saml2/soap.py |
|
32 |
except ImportError: |
32 |
except ImportError: |
33 |
#noinspection PyUnresolvedReferences |
33 |
#noinspection PyUnresolvedReferences |
34 |
from elementtree import ElementTree |
34 |
from elementtree import ElementTree |
|
|
35 |
import defusedxml.ElementTree |
35 |
|
36 |
|
36 |
|
37 |
|
37 |
logger = logging.getLogger(__name__) |
38 |
logger = logging.getLogger(__name__) |
Lines 146-152
def parse_soap_enveloped_saml_thingy(tex
|
Link Here
|
---|
|
146 |
:param expected_tags: What the tag of the SAML thingy is expected to be. |
147 |
:param expected_tags: What the tag of the SAML thingy is expected to be. |
147 |
:return: SAML thingy as a string |
148 |
:return: SAML thingy as a string |
148 |
""" |
149 |
""" |
149 |
envelope = ElementTree.fromstring(text) |
150 |
envelope = defusedxml.ElementTree.fromstring(text) |
150 |
|
151 |
|
151 |
# Make sure it's a SOAP message |
152 |
# Make sure it's a SOAP message |
152 |
assert envelope.tag == '{%s}Envelope' % soapenv.NAMESPACE |
153 |
assert envelope.tag == '{%s}Envelope' % soapenv.NAMESPACE |
Lines 196-202
def class_instances_from_soap_enveloped_
|
Link Here
|
---|
|
196 |
:return: The body and headers as class instances |
197 |
:return: The body and headers as class instances |
197 |
""" |
198 |
""" |
198 |
try: |
199 |
try: |
199 |
envelope = ElementTree.fromstring(text) |
200 |
envelope = defusedxml.ElementTree.fromstring(text) |
200 |
except Exception, exc: |
201 |
except Exception, exc: |
201 |
raise XmlParseError("%s" % exc) |
202 |
raise XmlParseError("%s" % exc) |
202 |
|
203 |
|
Lines 222-228
def open_soap_envelope(text):
|
Link Here
|
---|
|
222 |
:return: dictionary with two keys "body"/"header" |
223 |
:return: dictionary with two keys "body"/"header" |
223 |
""" |
224 |
""" |
224 |
try: |
225 |
try: |
225 |
envelope = ElementTree.fromstring(text) |
226 |
envelope = defusedxml.ElementTree.fromstring(text) |
226 |
except Exception, exc: |
227 |
except Exception, exc: |
227 |
raise XmlParseError("%s" % exc) |
228 |
raise XmlParseError("%s" % exc) |
228 |
|
229 |
|
229 |
-- python-pysaml2-2.0.0.orig/tests/test_03_saml2.py |
230 |
++ python-pysaml2-2.0.0/tests/test_03_saml2.py |
|
17 |
import cElementTree as ElementTree |
17 |
import cElementTree as ElementTree |
18 |
except ImportError: |
18 |
except ImportError: |
19 |
from elementtree import ElementTree |
19 |
from elementtree import ElementTree |
|
|
20 |
from defusedxml.common import EntitiesForbidden |
20 |
|
21 |
|
21 |
ITEMS = { |
22 |
ITEMS = { |
22 |
NameID: ["""<?xml version="1.0" encoding="utf-8"?> |
23 |
NameID: ["""<?xml version="1.0" encoding="utf-8"?> |
Lines 166-171
def test_create_class_from_xml_string_wr
|
Link Here
|
---|
|
166 |
assert kl == None |
167 |
assert kl == None |
167 |
|
168 |
|
168 |
|
169 |
|
|
|
170 |
def test_create_class_from_xml_string_xxe(): |
171 |
xml = """<?xml version="1.0"?> |
172 |
<!DOCTYPE lolz [ |
173 |
<!ENTITY lol "lol"> |
174 |
<!ELEMENT lolz (#PCDATA)> |
175 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
176 |
]> |
177 |
<lolz>&lol1;</lolz> |
178 |
""" |
179 |
with raises(EntitiesForbidden) as err: |
180 |
create_class_from_xml_string(NameID, xml) |
181 |
|
182 |
|
169 |
def test_ee_1(): |
183 |
def test_ee_1(): |
170 |
ee = saml2.extension_element_from_string( |
184 |
ee = saml2.extension_element_from_string( |
171 |
"""<?xml version='1.0' encoding='UTF-8'?><foo>bar</foo>""") |
185 |
"""<?xml version='1.0' encoding='UTF-8'?><foo>bar</foo>""") |
|
454 |
assert nid.text.strip() == "http://federationX.org" |
468 |
assert nid.text.strip() == "http://federationX.org" |
455 |
|
469 |
|
456 |
|
470 |
|
|
|
471 |
def test_ee_xxe(): |
472 |
xml = """<?xml version="1.0"?> |
473 |
<!DOCTYPE lolz [ |
474 |
<!ENTITY lol "lol"> |
475 |
<!ELEMENT lolz (#PCDATA)> |
476 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
477 |
]> |
478 |
<lolz>&lol1;</lolz> |
479 |
""" |
480 |
with raises(EntitiesForbidden): |
481 |
saml2.extension_element_from_string(xml) |
482 |
|
483 |
|
457 |
def test_extension_element_loadd(): |
484 |
def test_extension_element_loadd(): |
458 |
ava = {'attributes': {}, |
485 |
ava = {'attributes': {}, |
459 |
'tag': 'ExternalEntityAttributeAuthority', |
486 |
'tag': 'ExternalEntityAttributeAuthority', |
460 |
-- python-pysaml2-2.0.0.orig/tests/test_43_soap.py |
487 |
++ python-pysaml2-2.0.0/tests/test_43_soap.py |
|
12 |
import cElementTree as ElementTree |
12 |
import cElementTree as ElementTree |
13 |
except ImportError: |
13 |
except ImportError: |
14 |
from elementtree import ElementTree |
14 |
from elementtree import ElementTree |
|
|
15 |
from defusedxml.common import EntitiesForbidden |
16 |
|
17 |
from pytest import raises |
15 |
|
18 |
|
16 |
import saml2.samlp as samlp |
19 |
import saml2.samlp as samlp |
17 |
from saml2.samlp import NAMESPACE as SAMLP_NAMESPACE |
20 |
from saml2.samlp import NAMESPACE as SAMLP_NAMESPACE |
|
|
21 |
from saml2 import soap |
18 |
|
22 |
|
19 |
NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" |
23 |
NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/" |
20 |
|
24 |
|
Lines 66-68
def test_make_soap_envelope():
|
Link Here
|
---|
|
66 |
assert len(body) == 1 |
70 |
assert len(body) == 1 |
67 |
saml_part = body[0] |
71 |
saml_part = body[0] |
68 |
assert saml_part.tag == '{%s}AuthnRequest' % SAMLP_NAMESPACE |
72 |
assert saml_part.tag == '{%s}AuthnRequest' % SAMLP_NAMESPACE |
69 |
-- python-pysaml2-2.0.0.orig/tests/test_51_client.py |
73 |
|
|
|
74 |
|
75 |
def test_parse_soap_enveloped_saml_thingy_xxe(): |
76 |
xml = """<?xml version="1.0"?> |
77 |
<!DOCTYPE lolz [ |
78 |
<!ENTITY lol "lol"> |
79 |
<!ELEMENT lolz (#PCDATA)> |
80 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
81 |
]> |
82 |
<lolz>&lol1;</lolz> |
83 |
""" |
84 |
with raises(EntitiesForbidden): |
85 |
soap.parse_soap_enveloped_saml_thingy(xml, None) |
86 |
|
87 |
|
88 |
def test_class_instances_from_soap_enveloped_saml_thingies_xxe(): |
89 |
xml = """<?xml version="1.0"?> |
90 |
<!DOCTYPE lolz [ |
91 |
<!ENTITY lol "lol"> |
92 |
<!ELEMENT lolz (#PCDATA)> |
93 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
94 |
]> |
95 |
<lolz>&lol1;</lolz> |
96 |
""" |
97 |
with raises(soap.XmlParseError): |
98 |
soap.class_instances_from_soap_enveloped_saml_thingies(xml, None) |
99 |
|
100 |
|
101 |
def test_open_soap_envelope_xxe(): |
102 |
xml = """<?xml version="1.0"?> |
103 |
<!DOCTYPE lolz [ |
104 |
<!ENTITY lol "lol"> |
105 |
<!ELEMENT lolz (#PCDATA)> |
106 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
107 |
]> |
108 |
<lolz>&lol1;</lolz> |
109 |
""" |
110 |
with raises(soap.XmlParseError): |
111 |
soap.open_soap_envelope(xml) |
|
|
112 |
++ python-pysaml2-2.0.0/tests/test_51_client.py |
|
4 |
import base64 |
4 |
import base64 |
5 |
import urllib |
5 |
import urllib |
6 |
import urlparse |
6 |
import urlparse |
|
|
7 |
|
7 |
from saml2.authn_context import INTERNETPROTOCOLPASSWORD |
8 |
from saml2.authn_context import INTERNETPROTOCOLPASSWORD |
8 |
from saml2.response import LogoutResponse |
9 |
from saml2.response import LogoutResponse |
9 |
|
10 |
|
Lines 11-16
from saml2.client import Saml2Client
|
Link Here
|
---|
|
11 |
from saml2 import samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT |
12 |
from saml2 import samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT |
12 |
from saml2 import saml, config, class_name |
13 |
from saml2 import saml, config, class_name |
13 |
from saml2.config import SPConfig |
14 |
from saml2.config import SPConfig |
|
|
15 |
from saml2.pack import parse_soap_enveloped_saml |
14 |
from saml2.saml import NAMEID_FORMAT_PERSISTENT |
16 |
from saml2.saml import NAMEID_FORMAT_PERSISTENT |
15 |
from saml2.saml import NAMEID_FORMAT_TRANSIENT |
17 |
from saml2.saml import NAMEID_FORMAT_TRANSIENT |
16 |
from saml2.saml import NameID |
18 |
from saml2.saml import NameID |
Lines 18-23
from saml2.server import Server
|
Link Here
|
---|
|
18 |
from saml2.time_util import in_a_while |
20 |
from saml2.time_util import in_a_while |
19 |
|
21 |
|
20 |
from py.test import raises |
22 |
from py.test import raises |
|
|
23 |
from defusedxml.common import EntitiesForbidden |
21 |
from fakeIDP import FakeIDP, unpack_form |
24 |
from fakeIDP import FakeIDP, unpack_form |
22 |
|
25 |
|
23 |
|
26 |
|
Lines 439-444
class TestClientWithDummy():
|
Link Here
|
---|
|
439 |
'http://www.example.com/login' |
442 |
'http://www.example.com/login' |
440 |
assert ac.authn_context_class_ref.text == INTERNETPROTOCOLPASSWORD |
443 |
assert ac.authn_context_class_ref.text == INTERNETPROTOCOLPASSWORD |
441 |
|
444 |
|
|
|
445 |
def test_parse_soap_enveloped_saml_xxe(): |
446 |
xml = """<?xml version="1.0"?> |
447 |
<!DOCTYPE lolz [ |
448 |
<!ENTITY lol "lol"> |
449 |
<!ELEMENT lolz (#PCDATA)> |
450 |
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> |
451 |
]> |
452 |
<lolz>&lol1;</lolz> |
453 |
""" |
454 |
with raises(EntitiesForbidden): |
455 |
parse_soap_enveloped_saml(xml, None) |
442 |
|
456 |
|
443 |
# if __name__ == "__main__": |
457 |
# if __name__ == "__main__": |
444 |
# tc = TestClient() |
458 |
# tc = TestClient() |