@@ -220,7 +220,7 @@ def for_me(conditions, myself):
220
220
221
221
def authn_response (conf , return_addrs , outstanding_queries = None , timeslack = 0 ,
222
222
asynchop = True , allow_unsolicited = False ,
223
- want_assertions_signed = False ):
223
+ want_assertions_signed = False , conv_info = None ):
224
224
sec = security_context (conf )
225
225
if not timeslack :
226
226
try :
@@ -231,12 +231,13 @@ def authn_response(conf, return_addrs, outstanding_queries=None, timeslack=0,
231
231
return AuthnResponse (sec , conf .attribute_converters , conf .entityid ,
232
232
return_addrs , outstanding_queries , timeslack ,
233
233
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 )
235
236
236
237
237
238
# comes in over SOAP so synchronous
238
239
def attribute_response (conf , return_addrs , timeslack = 0 , asynchop = False ,
239
- test = False ):
240
+ test = False , conv_info = None ):
240
241
sec = security_context (conf )
241
242
if not timeslack :
242
243
try :
@@ -246,14 +247,14 @@ def attribute_response(conf, return_addrs, timeslack=0, asynchop=False,
246
247
247
248
return AttributeResponse (sec , conf .attribute_converters , conf .entityid ,
248
249
return_addrs , timeslack , asynchop = asynchop ,
249
- test = test )
250
+ test = test , conv_info = conv_info )
250
251
251
252
252
253
class StatusResponse (object ):
253
254
msgtype = "status_response"
254
255
255
256
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 ):
257
258
self .sec = sec_context
258
259
self .return_addrs = return_addrs
259
260
@@ -272,6 +273,7 @@ def __init__(self, sec_context, return_addrs=None, timeslack=0,
272
273
self .not_signed = False
273
274
self .asynchop = asynchop
274
275
self .do_not_verify = False
276
+ self .conv_info = conv_info or {}
275
277
276
278
def _clear (self ):
277
279
self .xmlstr = ""
@@ -429,19 +431,19 @@ class LogoutResponse(StatusResponse):
429
431
msgtype = "logout_response"
430
432
431
433
def __init__ (self , sec_context , return_addrs = None , timeslack = 0 ,
432
- asynchop = True ):
434
+ asynchop = True , conv_info = None ):
433
435
StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
434
- asynchop = asynchop )
436
+ asynchop = asynchop , conv_info = conv_info )
435
437
self .signature_check = self .sec .correctly_signed_logout_response
436
438
437
439
438
440
class NameIDMappingResponse (StatusResponse ):
439
441
msgtype = "name_id_mapping_response"
440
442
441
443
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 ):
443
445
StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
444
- request_id , asynchop )
446
+ request_id , asynchop , conv_info = conv_info )
445
447
self .signature_check = self .sec \
446
448
.correctly_signed_name_id_mapping_response
447
449
@@ -450,9 +452,9 @@ class ManageNameIDResponse(StatusResponse):
450
452
msgtype = "manage_name_id_response"
451
453
452
454
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 ):
454
456
StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
455
- request_id , asynchop )
457
+ request_id , asynchop , conv_info = conv_info )
456
458
self .signature_check = self .sec .correctly_signed_manage_name_id_response
457
459
458
460
@@ -469,10 +471,10 @@ def __init__(self, sec_context, attribute_converters, entity_id,
469
471
timeslack = 0 , asynchop = True , allow_unsolicited = False ,
470
472
test = False , allow_unknown_attributes = False ,
471
473
want_assertions_signed = False , want_response_signed = False ,
472
- ** kwargs ):
474
+ conv_info = None , ** kwargs ):
473
475
474
476
StatusResponse .__init__ (self , sec_context , return_addrs , timeslack ,
475
- asynchop = asynchop )
477
+ asynchop = asynchop , conv_info = conv_info )
476
478
self .entity_id = entity_id
477
479
self .attribute_converters = attribute_converters
478
480
if outstanding_queries :
@@ -721,6 +723,10 @@ def get_subject(self):
721
723
assert self .assertion .subject
722
724
subject = self .assertion .subject
723
725
subjconf = []
726
+
727
+ if not self .verify_attesting_entity (subject .subject_confirmation ):
728
+ raise VerificationError ("No valid attesting address" )
729
+
724
730
for subject_confirmation in subject .subject_confirmation :
725
731
_data = subject_confirmation .subject_confirmation_data
726
732
@@ -736,6 +742,10 @@ def get_subject(self):
736
742
raise ValueError ("Unknown subject confirmation method: %s" % (
737
743
subject_confirmation .method ,))
738
744
745
+ _recip = _data .recipient
746
+ if not _recip or not self .verify_recipient (_recip ):
747
+ raise VerificationError ("No valid recipient" )
748
+
739
749
subjconf .append (subject_confirmation )
740
750
741
751
if not subjconf :
@@ -933,7 +943,7 @@ def parse_assertion(self, keys=None):
933
943
decr_text_old = None
934
944
while (self .find_encrypt_data (
935
945
resp ) or self .find_encrypt_data_assertion_list (
936
- _enc_assertions )) and \
946
+ _enc_assertions )) and \
937
947
decr_text_old != decr_text :
938
948
decr_text_old = decr_text
939
949
decr_text = self .sec .decrypt_keys (decr_text , keys )
@@ -984,9 +994,11 @@ def parse_assertion(self, keys=None):
984
994
return True
985
995
986
996
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.
990
1002
"""
991
1003
992
1004
try :
@@ -1069,21 +1081,54 @@ def __str__(self):
1069
1081
return "%s" % self .xmlstr .decode ("utf-8" )
1070
1082
return "%s" % self .xmlstr
1071
1083
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
1073
1091
"""
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
1076
1102
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
1078
1116
:return: True/False
1079
1117
"""
1080
1118
1119
+ try :
1120
+ address = self .conv_info ['remote_addr' ]
1121
+ except KeyError :
1122
+ address = '0.0.0.0'
1123
+
1081
1124
correct = 0
1082
- for subject_conf in self . assertion . subject . subject_confirmation :
1125
+ for subject_conf in subject_confirmation :
1083
1126
if subject_conf .subject_confirmation_data is None :
1084
1127
correct += 1 # In reality undefined
1085
1128
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 :
1087
1132
correct += 1
1088
1133
else :
1089
1134
correct += 1
@@ -1098,10 +1143,12 @@ class AuthnQueryResponse(AuthnResponse):
1098
1143
msgtype = "authn_query_response"
1099
1144
1100
1145
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 ):
1102
1148
AuthnResponse .__init__ (self , sec_context , attribute_converters ,
1103
1149
entity_id , return_addrs , timeslack = timeslack ,
1104
- asynchop = asynchop , test = test )
1150
+ asynchop = asynchop , test = test ,
1151
+ conv_info = conv_info )
1105
1152
self .entity_id = entity_id
1106
1153
self .attribute_converters = attribute_converters
1107
1154
self .assertion = None
@@ -1115,10 +1162,12 @@ class AttributeResponse(AuthnResponse):
1115
1162
msgtype = "attribute_response"
1116
1163
1117
1164
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 ):
1119
1167
AuthnResponse .__init__ (self , sec_context , attribute_converters ,
1120
1168
entity_id , return_addrs , timeslack = timeslack ,
1121
- asynchop = asynchop , test = test )
1169
+ asynchop = asynchop , test = test ,
1170
+ conv_info = conv_info )
1122
1171
self .entity_id = entity_id
1123
1172
self .attribute_converters = attribute_converters
1124
1173
self .assertion = None
@@ -1131,10 +1180,11 @@ class AuthzResponse(AuthnResponse):
1131
1180
msgtype = "authz_decision_response"
1132
1181
1133
1182
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 ):
1135
1185
AuthnResponse .__init__ (self , sec_context , attribute_converters ,
1136
1186
entity_id , return_addrs , timeslack = timeslack ,
1137
- asynchop = asynchop )
1187
+ asynchop = asynchop , conv_info = conv_info )
1138
1188
self .entity_id = entity_id
1139
1189
self .attribute_converters = attribute_converters
1140
1190
self .assertion = None
@@ -1145,10 +1195,12 @@ class ArtifactResponse(AuthnResponse):
1145
1195
msgtype = "artifact_response"
1146
1196
1147
1197
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 ):
1149
1200
AuthnResponse .__init__ (self , sec_context , attribute_converters ,
1150
1201
entity_id , return_addrs , timeslack = timeslack ,
1151
- asynchop = asynchop , test = test )
1202
+ asynchop = asynchop , test = test ,
1203
+ conv_info = conv_info )
1152
1204
self .entity_id = entity_id
1153
1205
self .attribute_converters = attribute_converters
1154
1206
self .assertion = None
@@ -1158,7 +1210,7 @@ def __init__(self, sec_context, attribute_converters, entity_id,
1158
1210
def response_factory (xmlstr , conf , return_addrs = None , outstanding_queries = None ,
1159
1211
timeslack = 0 , decode = True , request_id = 0 , origxml = None ,
1160
1212
asynchop = True , allow_unsolicited = False ,
1161
- want_assertions_signed = False ):
1213
+ want_assertions_signed = False , conv_info = None ):
1162
1214
sec_context = security_context (conf )
1163
1215
if not timeslack :
1164
1216
try :
@@ -1171,23 +1223,23 @@ def response_factory(xmlstr, conf, return_addrs=None, outstanding_queries=None,
1171
1223
extension_schema = conf .extension_schema
1172
1224
1173
1225
response = StatusResponse (sec_context , return_addrs , timeslack , request_id ,
1174
- asynchop )
1226
+ asynchop , conv_info = conv_info )
1175
1227
try :
1176
1228
response .loads (xmlstr , decode , origxml )
1177
1229
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 )
1184
1236
authnresp .update (response )
1185
1237
return authnresp
1186
1238
except TypeError :
1187
1239
response .signature_check = sec_context .correctly_signed_logout_response
1188
1240
response .loads (xmlstr , decode , origxml )
1189
1241
logoutresp = LogoutResponse (sec_context , return_addrs , timeslack ,
1190
- asynchop = asynchop )
1242
+ asynchop = asynchop , conv_info = conv_info )
1191
1243
logoutresp .update (response )
1192
1244
return logoutresp
1193
1245
0 commit comments