From b0902626cdb61ebc316dca12608927ff5da155dc Mon Sep 17 00:00:00 2001 From: peppelinux <giuseppe.demarco@unical.it> Date: Sun, 21 Mar 2021 01:16:11 +0100 Subject: [PATCH 1/3] fix: assertion format check --- src/saml2/response.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/saml2/response.py b/src/saml2/response.py index 26963a04e..92bac1085 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -394,6 +394,23 @@ def issue_instant_ok(self): issued_at = str_to_time(self.response.issue_instant) return lower < issued_at < upper + def issuer_ok(self): + """ Check if the issuer have a valid Format, additional check may be implemented""" + if self.response.issuer and self.response.issuer.format: + if self.response.issuer.format != saml.NAMEID_FORMAT_ENTITY: + return False + return True + + def assertion_ok(self): + """ Check assetions attributes, additional checks may be implemented """ + valid = True + if hasattr(self.response, 'assertion'): + for ass in self.response.assertion: + if ass.version and ass.version != '2.0': + valid = False + break + return valid + def _verify(self): if self.request_id and self.in_response_to and \ self.in_response_to != self.request_id: @@ -416,7 +433,14 @@ def _verify(self): logger.error("%s not in %s", self.response.destination, self.return_addrs) return None - valid = self.issue_instant_ok() and self.status_ok() + valid = all( + ( + self.issue_instant_ok(), + self.issuer_ok(), + self.status_ok(), + self.assertion_ok() + ) + ) return valid def loads(self, xmldata, decode=True, origxml=None): @@ -1116,7 +1140,7 @@ def session_info(self): raise StatusInvalidAuthnResponseStatement( "The Authn Response Statement is not valid" ) - + def __str__(self): return self.xmlstr From 130834cd3676764f7e6f63aa48eadd02b9a7caad Mon Sep 17 00:00:00 2001 From: peppelinux <giuseppe.demarco@unical.it> Date: Sun, 21 Mar 2021 02:13:24 +0100 Subject: [PATCH 2/3] Additional checks on assertion sub elements --- src/saml2/response.py | 54 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/saml2/response.py b/src/saml2/response.py index 92bac1085..b9ea71d2c 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -383,7 +383,7 @@ def status_ok(self): logger.info(msg) raise err_cls(msg) - def issue_instant_ok(self): + def issue_instant_ok(self, issue_instant): """ Check that the response was issued at a reasonable time """ upper = time_util.shift_time(time_util.time_in_a_while(days=1), self.timeslack).timetuple() @@ -391,7 +391,7 @@ def issue_instant_ok(self): -self.timeslack).timetuple() # print("issue_instant: %s" % self.response.issue_instant) # print("%s < x < %s" % (lower, upper)) - issued_at = str_to_time(self.response.issue_instant) + issued_at = str_to_time(issue_instant) return lower < issued_at < upper def issuer_ok(self): @@ -402,13 +402,55 @@ def issuer_ok(self): return True def assertion_ok(self): - """ Check assetions attributes, additional checks may be implemented """ + """ Check assertions attributes, additional checks may be implemented + + Commented check may be too pedantic + """ valid = True if hasattr(self.response, 'assertion'): + # breakpoint() for ass in self.response.assertion: + # Version if ass.version and ass.version != '2.0': - valid = False - break + raise VersionMismatch(f'{ass.version}') + # IssueInstant + # if hasattr(ass, 'issue_instant') and not self.issue_instant_ok(ass.issue_instant): + # breakpoint() + # raise Exception('Invalid Issue Instant') + # NameQualifier + # if not hasattr(ass.subject.name_id, 'name_qualifier') or \ + # not ass.subject.name_id.name_qualifier: + # raise Exception('Not a valid subject.name_id.name_qualifier') + if hasattr(ass.subject.name_id, 'format') and not ass.subject.name_id.format: + raise Exception('Not a valid subject.name_id.format') + if hasattr(ass.subject.name_id, 'format'): + if ass.subject.name_id.format not in dict(saml.NAMEID_FORMATS_SAML2).values(): + msg = 'Not a valid subject.name_id.format: {}' + raise Exception(msg.format(ass.subject.name_id.format)) + + # subject confirmation + for subject_confirmation in ass.subject.subject_confirmation: + if not hasattr(subject_confirmation, 'subject_confirmation_data') or \ + not getattr(subject_confirmation, 'subject_confirmation_data', None): + msg = 'subject_confirmation_data not present' + raise Exception(msg) + + if not subject_confirmation.subject_confirmation_data.in_response_to: + raise Exception('subject.subject_confirmation_data in response -> null data') + + # TODO: match to the recipient + # if self.recipient != subject_confirmation.subject_confirmation_data.recipient: + # msg = 'subject_confirmation_data.recipient not valid: {}' + # raise Exception(msg.format(subject_confirmation_data.recipient)) + + if not hasattr(subject_confirmation.subject_confirmation_data, 'not_on_or_after') or \ + not getattr(subject_confirmation.subject_confirmation_data, 'not_on_or_after', None): + raise Exception('subject.subject_confirmation_data not_on_or_after not valid') + + if not hasattr(subject_confirmation.subject_confirmation_data, 'in_response_to') or \ + not getattr(subject_confirmation.subject_confirmation_data, 'in_response_to', None): + raise Exception('subject.subject_confirmation_data in response to not valid') + return valid def _verify(self): @@ -435,7 +477,7 @@ def _verify(self): valid = all( ( - self.issue_instant_ok(), + self.issue_instant_ok(self.response.issue_instant), self.issuer_ok(), self.status_ok(), self.assertion_ok() From 56dd9655cdc53fc19e1e73202c51c821f4b127d5 Mon Sep 17 00:00:00 2001 From: peppelinux <giuseppe.demarco@unical.it> Date: Sun, 21 Mar 2021 02:25:33 +0100 Subject: [PATCH 3/3] clean up --- src/saml2/response.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/saml2/response.py b/src/saml2/response.py index b9ea71d2c..968639434 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -421,9 +421,10 @@ def assertion_ok(self): # if not hasattr(ass.subject.name_id, 'name_qualifier') or \ # not ass.subject.name_id.name_qualifier: # raise Exception('Not a valid subject.name_id.name_qualifier') - if hasattr(ass.subject.name_id, 'format') and not ass.subject.name_id.format: - raise Exception('Not a valid subject.name_id.format') if hasattr(ass.subject.name_id, 'format'): + if not ass.subject.name_id.format: + raise Exception('Not a valid subject.name_id.format') + if ass.subject.name_id.format not in dict(saml.NAMEID_FORMATS_SAML2).values(): msg = 'Not a valid subject.name_id.format: {}' raise Exception(msg.format(ass.subject.name_id.format)) @@ -447,10 +448,6 @@ def assertion_ok(self): not getattr(subject_confirmation.subject_confirmation_data, 'not_on_or_after', None): raise Exception('subject.subject_confirmation_data not_on_or_after not valid') - if not hasattr(subject_confirmation.subject_confirmation_data, 'in_response_to') or \ - not getattr(subject_confirmation.subject_confirmation_data, 'in_response_to', None): - raise Exception('subject.subject_confirmation_data in response to not valid') - return valid def _verify(self):