@@ -220,7 +220,7 @@ def for_me(conditions, myself):
220220
221221def authn_response (conf , return_addrs , outstanding_queries = None , timeslack = 0 ,
222222 asynchop = True , allow_unsolicited = False ,
223- want_assertions_signed = False ):
223+ want_assertions_signed = False , conv_info = None ):
224224 sec = security_context (conf )
225225 if not timeslack :
226226 try :
@@ -231,12 +231,13 @@ def authn_response(conf, return_addrs, outstanding_queries=None, timeslack=0,
231231 return AuthnResponse (sec , conf .attribute_converters , conf .entityid ,
232232 return_addrs , outstanding_queries , timeslack ,
233233 asynchop = asynchop , allow_unsolicited = allow_unsolicited ,
234- want_assertions_signed = want_assertions_signed )
234+ want_assertions_signed = want_assertions_signed ,
235+ conv_info = conv_info )
235236
236237
237238# comes in over SOAP so synchronous
238239def attribute_response (conf , return_addrs , timeslack = 0 , asynchop = False ,
239- test = False ):
240+ test = False , conv_info = None ):
240241 sec = security_context (conf )
241242 if not timeslack :
242243 try :
@@ -246,14 +247,14 @@ def attribute_response(conf, return_addrs, timeslack=0, asynchop=False,
246247
247248 return AttributeResponse (sec , conf .attribute_converters , conf .entityid ,
248249 return_addrs , timeslack , asynchop = asynchop ,
249- test = test )
250+ test = test , conv_info = conv_info )
250251
251252
252253class StatusResponse (object ):
253254 msgtype = "status_response"
254255
255256 def __init__ (self , sec_context , return_addrs = None , timeslack = 0 ,
256- request_id = 0 , asynchop = True ):
257+ request_id = 0 , asynchop = True , conv_info = None ):
257258 self .sec = sec_context
258259 self .return_addrs = return_addrs
259260
@@ -272,6 +273,7 @@ def __init__(self, sec_context, return_addrs=None, timeslack=0,
272273 self .not_signed = False
273274 self .asynchop = asynchop
274275 self .do_not_verify = False
276+ self .conv_info = conv_info or {}
275277
276278 def _clear (self ):
277279 self .xmlstr = ""
@@ -429,19 +431,19 @@ class LogoutResponse(StatusResponse):
429431 msgtype = "logout_response"
430432
431433 def __init__ (self , sec_context , return_addrs = None , timeslack = 0 ,
432- asynchop = True ):
434+ asynchop = True , conv_info = None ):
433435 StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
434- asynchop = asynchop )
436+ asynchop = asynchop , conv_info = conv_info )
435437 self .signature_check = self .sec .correctly_signed_logout_response
436438
437439
438440class NameIDMappingResponse (StatusResponse ):
439441 msgtype = "name_id_mapping_response"
440442
441443 def __init__ (self , sec_context , return_addrs = None , timeslack = 0 ,
442- request_id = 0 , asynchop = True ):
444+ request_id = 0 , asynchop = True , conv_info = None ):
443445 StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
444- request_id , asynchop )
446+ request_id , asynchop , conv_info = conv_info )
445447 self .signature_check = self .sec \
446448 .correctly_signed_name_id_mapping_response
447449
@@ -450,9 +452,9 @@ class ManageNameIDResponse(StatusResponse):
450452 msgtype = "manage_name_id_response"
451453
452454 def __init__ (self , sec_context , return_addrs = None , timeslack = 0 ,
453- request_id = 0 , asynchop = True ):
455+ request_id = 0 , asynchop = True , conv_info = None ):
454456 StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
455- request_id , asynchop )
457+ request_id , asynchop , conv_info = conv_info )
456458 self .signature_check = self .sec .correctly_signed_manage_name_id_response
457459
458460
@@ -469,10 +471,10 @@ def __init__(self, sec_context, attribute_converters, entity_id,
469471 timeslack = 0 , asynchop = True , allow_unsolicited = False ,
470472 test = False , allow_unknown_attributes = False ,
471473 want_assertions_signed = False , want_response_signed = False ,
472- ** kwargs ):
474+ conv_info = None , ** kwargs ):
473475
474476 StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
475- asynchop = asynchop )
477+ asynchop = asynchop , conv_info = conv_info )
476478 self .entity_id = entity_id
477479 self .attribute_converters = attribute_converters
478480 if outstanding_queries :
@@ -721,6 +723,10 @@ def get_subject(self):
721723 assert self .assertion .subject
722724 subject = self .assertion .subject
723725 subjconf = []
726+
727+ if not self .verify_attesting_entity (subject .subject_confirmation ):
728+ raise VerificationError ("No valid attesting address" )
729+
724730 for subject_confirmation in subject .subject_confirmation :
725731 _data = subject_confirmation .subject_confirmation_data
726732
@@ -736,6 +742,10 @@ def get_subject(self):
736742 raise ValueError ("Unknown subject confirmation method: %s" % (
737743 subject_confirmation .method ,))
738744
745+ _recip = _data .recipient
746+ if not _recip or not self .verify_recipient (_recip ):
747+ raise VerificationError ("No valid recipient" )
748+
739749 subjconf .append (subject_confirmation )
740750
741751 if not subjconf :
@@ -933,7 +943,7 @@ def parse_assertion(self, keys=None):
933943 decr_text_old = None
934944 while (self .find_encrypt_data (
935945 resp ) or self .find_encrypt_data_assertion_list (
936- _enc_assertions )) and \
946+ _enc_assertions )) and \
937947 decr_text_old != decr_text :
938948 decr_text_old = decr_text
939949 decr_text = self .sec .decrypt_keys (decr_text , keys )
@@ -984,9 +994,11 @@ def parse_assertion(self, keys=None):
984994 return True
985995
986996 def verify (self , keys = None ):
987- """ Verify that the assertion is syntactically correct and
988- the signature is correct if present.
989- :param key_file: If not the default key file should be used this is it.
997+ """ Verify that the assertion is syntactically correct and the
998+ signature is correct if present.
999+
1000+ :param keys: If not the default key file should be used then use one
1001+ of these.
9901002 """
9911003
9921004 try :
@@ -1069,21 +1081,54 @@ def __str__(self):
10691081 return "%s" % self .xmlstr .decode ("utf-8" )
10701082 return "%s" % self .xmlstr
10711083
1072- def verify_attesting_entity (self , address ):
1084+ def verify_recipient (self , recipient ):
1085+ """
1086+ Verify that I'm the recipient of the assertion
1087+
1088+ :param recipient: A URI specifying the entity or location to which an
1089+ attesting entity can present the assertion.
1090+ :return: True/False
10731091 """
1074- Assumes one assertion. At least one address specification has to be
1075- correct.
1092+ if not self .conv_info :
1093+ return True
1094+
1095+ _info = self .conv_info
1096+
1097+ try :
1098+ if recipient == _info ['entity_id' ]:
1099+ return True
1100+ except KeyError :
1101+ pass
10761102
1077- :param address: IP address of attesting entity
1103+ try :
1104+ if recipient in self .return_addrs :
1105+ return True
1106+ except KeyError :
1107+ pass
1108+
1109+ return False
1110+
1111+ def verify_attesting_entity (self , subject_confirmation ):
1112+ """
1113+ At least one address specification has to be correct.
1114+
1115+ :param subject_confirmation: A SubbjectConfirmation instance
10781116 :return: True/False
10791117 """
10801118
1119+ try :
1120+ address = self .conv_info ['remote_addr' ]
1121+ except KeyError :
1122+ address = '0.0.0.0'
1123+
10811124 correct = 0
1082- for subject_conf in self . assertion . subject . subject_confirmation :
1125+ for subject_conf in subject_confirmation :
10831126 if subject_conf .subject_confirmation_data is None :
10841127 correct += 1 # In reality undefined
10851128 elif subject_conf .subject_confirmation_data .address :
1086- if subject_conf .subject_confirmation_data .address == address :
1129+ if address == '0.0.0.0' : # accept anything
1130+ correct += 1
1131+ elif subject_conf .subject_confirmation_data .address == address :
10871132 correct += 1
10881133 else :
10891134 correct += 1
@@ -1098,10 +1143,12 @@ class AuthnQueryResponse(AuthnResponse):
10981143 msgtype = "authn_query_response"
10991144
11001145 def __init__ (self , sec_context , attribute_converters , entity_id ,
1101- return_addrs = None , timeslack = 0 , asynchop = False , test = False ):
1146+ return_addrs = None , timeslack = 0 , asynchop = False , test = False ,
1147+ conv_info = None ):
11021148 AuthnResponse .__init__ (self , sec_context , attribute_converters ,
11031149 entity_id , return_addrs , timeslack = timeslack ,
1104- asynchop = asynchop , test = test )
1150+ asynchop = asynchop , test = test ,
1151+ conv_info = conv_info )
11051152 self .entity_id = entity_id
11061153 self .attribute_converters = attribute_converters
11071154 self .assertion = None
@@ -1115,10 +1162,12 @@ class AttributeResponse(AuthnResponse):
11151162 msgtype = "attribute_response"
11161163
11171164 def __init__ (self , sec_context , attribute_converters , entity_id ,
1118- return_addrs = None , timeslack = 0 , asynchop = False , test = False ):
1165+ return_addrs = None , timeslack = 0 , asynchop = False , test = False ,
1166+ conv_info = None ):
11191167 AuthnResponse .__init__ (self , sec_context , attribute_converters ,
11201168 entity_id , return_addrs , timeslack = timeslack ,
1121- asynchop = asynchop , test = test )
1169+ asynchop = asynchop , test = test ,
1170+ conv_info = conv_info )
11221171 self .entity_id = entity_id
11231172 self .attribute_converters = attribute_converters
11241173 self .assertion = None
@@ -1131,10 +1180,11 @@ class AuthzResponse(AuthnResponse):
11311180 msgtype = "authz_decision_response"
11321181
11331182 def __init__ (self , sec_context , attribute_converters , entity_id ,
1134- return_addrs = None , timeslack = 0 , asynchop = False ):
1183+ return_addrs = None , timeslack = 0 , asynchop = False ,
1184+ conv_info = None ):
11351185 AuthnResponse .__init__ (self , sec_context , attribute_converters ,
11361186 entity_id , return_addrs , timeslack = timeslack ,
1137- asynchop = asynchop )
1187+ asynchop = asynchop , conv_info = conv_info )
11381188 self .entity_id = entity_id
11391189 self .attribute_converters = attribute_converters
11401190 self .assertion = None
@@ -1145,10 +1195,12 @@ class ArtifactResponse(AuthnResponse):
11451195 msgtype = "artifact_response"
11461196
11471197 def __init__ (self , sec_context , attribute_converters , entity_id ,
1148- return_addrs = None , timeslack = 0 , asynchop = False , test = False ):
1198+ return_addrs = None , timeslack = 0 , asynchop = False , test = False ,
1199+ conv_info = None ):
11491200 AuthnResponse .__init__ (self , sec_context , attribute_converters ,
11501201 entity_id , return_addrs , timeslack = timeslack ,
1151- asynchop = asynchop , test = test )
1202+ asynchop = asynchop , test = test ,
1203+ conv_info = conv_info )
11521204 self .entity_id = entity_id
11531205 self .attribute_converters = attribute_converters
11541206 self .assertion = None
@@ -1158,7 +1210,7 @@ def __init__(self, sec_context, attribute_converters, entity_id,
11581210def response_factory (xmlstr , conf , return_addrs = None , outstanding_queries = None ,
11591211 timeslack = 0 , decode = True , request_id = 0 , origxml = None ,
11601212 asynchop = True , allow_unsolicited = False ,
1161- want_assertions_signed = False ):
1213+ want_assertions_signed = False , conv_info = None ):
11621214 sec_context = security_context (conf )
11631215 if not timeslack :
11641216 try :
@@ -1171,23 +1223,23 @@ def response_factory(xmlstr, conf, return_addrs=None, outstanding_queries=None,
11711223 extension_schema = conf .extension_schema
11721224
11731225 response = StatusResponse (sec_context , return_addrs , timeslack , request_id ,
1174- asynchop )
1226+ asynchop , conv_info = conv_info )
11751227 try :
11761228 response .loads (xmlstr , decode , origxml )
11771229 if response .response .assertion or response .response .encrypted_assertion :
1178- authnresp = AuthnResponse (sec_context , attribute_converters ,
1179- entity_id , return_addrs ,
1180- outstanding_queries , timeslack , asynchop ,
1181- allow_unsolicited ,
1182- extension_schema = extension_schema ,
1183- want_assertions_signed = want_assertions_signed )
1230+ authnresp = AuthnResponse (
1231+ sec_context , attribute_converters , entity_id , return_addrs ,
1232+ outstanding_queries , timeslack , asynchop , allow_unsolicited ,
1233+ extension_schema = extension_schema ,
1234+ want_assertions_signed = want_assertions_signed ,
1235+ conv_info = conv_info )
11841236 authnresp .update (response )
11851237 return authnresp
11861238 except TypeError :
11871239 response .signature_check = sec_context .correctly_signed_logout_response
11881240 response .loads (xmlstr , decode , origxml )
11891241 logoutresp = LogoutResponse (sec_context , return_addrs , timeslack ,
1190- asynchop = asynchop )
1242+ asynchop = asynchop , conv_info = conv_info )
11911243 logoutresp .update (response )
11921244 return logoutresp
11931245
0 commit comments