Skip to content

Add support for xmlsec1 1.3 #902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions src/saml2/sigver.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,18 +471,25 @@ def import_rsa_key_from_file(filename):
return key


def parse_xmlsec_output(output):
def parse_xmlsec_verify_output(output, version=None):
"""Parse the output from xmlsec to try to find out if the
command was successfull or not.

:param output: The output from Popen
:return: A boolean; True if the command was a success otherwise False
"""
for line in output.splitlines():
if line == "OK":
return True
elif line == "FAIL":
raise XmlsecError(output)
if version is None or version < (1, 3):
for line in output.splitlines():
if line == "OK":
return True
elif line == "FAIL":
raise XmlsecError(output)
else:
for line in output.splitlines():
if line == 'Verification status: OK':
return True
elif line == 'Verification status: FAILED':
raise XmlsecError(output)
raise XmlsecError(output)


Expand Down Expand Up @@ -593,9 +600,18 @@ def verify_redirect_signature(saml_msg, crypto, cert=None, sigkey=None):


class CryptoBackend:
@property
def version(self):
raise NotImplementedError()

@property
def version_nums(self):
try:
vns = tuple(int(t) for t in self.version)
except ValueError:
vns = (0, 0, 0)
return vns

def encrypt(self, text, recv_key, template, key_type):
raise NotImplementedError()

Expand Down Expand Up @@ -634,6 +650,7 @@ def __init__(self, xmlsec_binary, delete_tmpfiles=True, **kwargs):
except KeyError:
pass

@property
def version(self):
com_list = [self.xmlsec, "--version"]
pof = Popen(com_list, stderr=PIPE, stdout=PIPE)
Expand All @@ -642,7 +659,7 @@ def version(self):
try:
return content.split(" ")[1]
except IndexError:
return ""
return "0.0.0"

def encrypt(self, text, recv_key, template, session_key_type, xpath=""):
"""
Expand Down Expand Up @@ -824,7 +841,7 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i
except XmlsecError as e:
raise SignatureError(com_list) from e

return parse_xmlsec_output(stderr)
return parse_xmlsec_verify_output(stderr, self.version_nums)

def _run_xmlsec(self, com_list, extra_args):
"""
Expand All @@ -836,6 +853,8 @@ def _run_xmlsec(self, com_list, extra_args):
"""
with NamedTemporaryFile(suffix=".xml") as ntf:
com_list.extend(["--output", ntf.name])
if self.version_nums >= (1, 3):
com_list.extend(['--lax-key-search'])
com_list += extra_args

logger.debug("xmlsec command: %s", " ".join(com_list))
Expand Down Expand Up @@ -870,10 +889,13 @@ class CryptoBackendXMLSecurity(CryptoBackend):
def __init__(self):
CryptoBackend.__init__(self)

@property
def version(self):
# XXX if XMLSecurity.__init__ included a __version__, that would be
# better than static 0.0 here.
return "XMLSecurity 0.0"
try:
import xmlsec
return xmlsec.__version__
except (ImportError, AttributeError):
return "0.0.0"

def sign_statement(self, statement, node_name, key_file, node_id):
"""
Expand Down
32 changes: 24 additions & 8 deletions tests/test_40_sigver.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def test_sign_assertion(self):
assert sass.id == "id-11111"
assert time_util.str_to_time(sass.issue_instant)

print(f"Crypto version : {self.sec.crypto.version()}")
print(f"Crypto version : {self.sec.crypto.version}")

item = self.sec.check_signature(sass, class_name(sass), sign_ass)

Expand All @@ -209,7 +209,7 @@ def test_multiple_signatures_assertion(self):
assert sass.id == "id-11111"
assert time_util.str_to_time(sass.issue_instant)

print(f"Crypto version : {self.sec.crypto.version()}")
print(f"Crypto version : {self.sec.crypto.version}")

item = self.sec.check_signature(sass, class_name(sass), sign_ass, must=True)

Expand Down Expand Up @@ -498,7 +498,7 @@ def test_sign_assertion(self):
assert sass.id == "id-11111"
assert time_util.str_to_time(sass.issue_instant)

print(f"Crypto version : {self.sec.crypto.version()}")
print(f"Crypto version : {self.sec.crypto.version}")

item = self.sec.check_signature(sass, class_name(sass), sign_ass)

Expand All @@ -515,7 +515,7 @@ def test_multiple_signatures_assertion(self):
assert sass.id == "id-11111"
assert time_util.str_to_time(sass.issue_instant)

print(f"Crypto version : {self.sec.crypto.version()}")
print(f"Crypto version : {self.sec.crypto.version}")

item = self.sec.check_signature(sass, class_name(sass), sign_ass, must=True)

Expand Down Expand Up @@ -1079,18 +1079,34 @@ def test_sha256_signing_non_ascii_ava():

def test_xmlsec_output_line_parsing():
output1 = "prefix\nOK\npostfix"
assert sigver.parse_xmlsec_output(output1)
assert sigver.parse_xmlsec_verify_output(output1)

output2 = "prefix\nFAIL\npostfix"
with raises(sigver.XmlsecError):
sigver.parse_xmlsec_output(output2)
sigver.parse_xmlsec_verify_output(output2)

output3 = "prefix\r\nOK\r\npostfix"
assert sigver.parse_xmlsec_output(output3)
assert sigver.parse_xmlsec_verify_output(output3)

output4 = "prefix\r\nFAIL\r\npostfix"
with raises(sigver.XmlsecError):
sigver.parse_xmlsec_output(output4)
sigver.parse_xmlsec_verify_output(output4)


def test_xmlsec_v1_3_x_output_line_parsing():
output1 = "prefix\nVerification status: OK\npostfix"
assert sigver.parse_xmlsec_verify_output(output1, version=(1, 3))

output2 = "prefix\nVerification status: FAILED\npostfix"
with raises(sigver.XmlsecError):
sigver.parse_xmlsec_verify_output(output2, version=(1, 3))

output3 = "prefix\r\nVerification status: OK\r\npostfix"
assert sigver.parse_xmlsec_verify_output(output3, version=(1, 3))

output4 = "prefix\r\nVerification status: FAILED\r\npostfix"
with raises(sigver.XmlsecError):
sigver.parse_xmlsec_verify_output(output4, version=(1, 3))


def test_cert_trailing_newlines_ignored():
Expand Down