Skip to content

Commit 82ee848

Browse files
author
Hans Hörberg
committed
Only validate certificate and set client certificate tp authn request.
Made it possible for the IdP to only validate the certificate without verifying the signature. This is needed when the proxy sends the SP certificate to the IdP. Made it possible to send the certificate that should be used during the creating of the authn request.
1 parent 234ce01 commit 82ee848

File tree

9 files changed

+59
-20
lines changed

9 files changed

+59
-20
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,9 @@ example/sp/sp_nocert2.xml
149149
example/sp/test.py
150150

151151
example/sp/sp_conf.py
152+
153+
example/sp/nocert_sp_conf/sp.xml
154+
155+
example/sp/nocert_sp_conf/sp_conf.py
156+
157+
example/sp/nocert_sp_conf/who.ini

src/saml2/client_base.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ def create_authn_request(self, destination, vorg="", scoping=None,
231231
:param kwargs: Extra key word arguments
232232
:return: <samlp:AuthnRequest> instance
233233
"""
234-
234+
client_crt = None
235+
if "client_crt" in kwargs:
236+
client_crt = kwargs["client_crt"]
235237
args = {}
236238
try:
237239
args["assertion_consumer_service_url"] = kwargs[
@@ -299,9 +301,11 @@ def create_authn_request(self, destination, vorg="", scoping=None,
299301
except KeyError:
300302
pass
301303

302-
if sign and self.sec.cert_handler.generate_cert():
304+
if (sign and self.sec.cert_handler.generate_cert()) or client_crt is not None:
303305
with self.lock:
304-
self.sec.cert_handler.update_cert(True)
306+
self.sec.cert_handler.update_cert(True, client_crt)
307+
if client_crt is not None:
308+
sign_prepare = True
305309
return self._message(AuthnRequest, destination, message_id, consent,
306310
extensions, sign, sign_prepare,
307311
protocol_binding=binding,

src/saml2/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
AA_IDP_ARGS = [
9393
"sign_assertion",
9494
"want_authn_requests_signed",
95+
"want_authn_requests_only_with_valid_cert",
9596
"provided_attributes",
9697
"subject_data",
9798
"sp",

src/saml2/entity.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,12 @@ def _parse_request(self, xmlstr, request_cls, service, binding):
546546
origdoc = xmlstr
547547
xmlstr = self.unravel(xmlstr, binding, request_cls.msgtype)
548548
must = self.config.getattr("want_authn_requests_signed", "idp")
549-
_request = _request.loads(xmlstr, binding, origdoc=origdoc, must=must)
549+
only_valid_cert = self.config.getattr("want_authn_requests_only_with_valid_cert", "idp")
550+
if only_valid_cert is None:
551+
only_valid_cert = False
552+
if only_valid_cert:
553+
must = True
554+
_request = _request.loads(xmlstr, binding, origdoc=origdoc, must=must, only_valid_cert=only_valid_cert)
550555

551556
_log_debug("Loaded request")
552557

src/saml2/md.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,8 @@ class IDPSSODescriptorType_(SSODescriptorType_):
10411041
c_cardinality['attribute'] = {"min": 0}
10421042
c_attributes['WantAuthnRequestsSigned'] = ('want_authn_requests_signed',
10431043
'boolean', False)
1044+
c_attributes['WantAuthnRequestsOnlyWithValidCert'] = ('want_authn_requests_only_with_valid_cert',
1045+
'boolean', False)
10441046
c_child_order.extend(['single_sign_on_service', 'name_id_mapping_service',
10451047
'assertion_id_request_service', 'attribute_profile',
10461048
'attribute'])
@@ -1069,6 +1071,7 @@ def __init__(self,
10691071
text=None,
10701072
extension_elements=None,
10711073
extension_attributes=None,
1074+
want_authn_requests_only_with_valid_cert=None,
10721075
):
10731076
SSODescriptorType_.__init__(self,
10741077
artifact_resolution_service=artifact_resolution_service,
@@ -1095,6 +1098,7 @@ def __init__(self,
10951098
self.attribute_profile = attribute_profile or []
10961099
self.attribute = attribute or []
10971100
self.want_authn_requests_signed = want_authn_requests_signed
1101+
self.want_authn_requests_only_with_valid_cert = want_authn_requests_only_with_valid_cert
10981102

10991103

11001104
def idpsso_descriptor_type__from_string(xml_string):
@@ -2012,3 +2016,5 @@ def entities_descriptor_from_string(xml_string):
20122016
def factory(tag, **kwargs):
20132017
return ELEMENT_BY_TAG[tag](**kwargs)
20142018

2019+
2020+

src/saml2/metadata.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"want_assertions_signed": "true",
3838
"authn_requests_signed": "false",
3939
"want_authn_requests_signed": "true",
40+
"want_authn_requests_only_with_valid_cert": "false",
4041
}
4142

4243
ORG_ATTR_TRANSL = {
@@ -407,6 +408,7 @@ def do_endpoints(conf, endpoints):
407408
"want_assertions_signed": "true",
408409
"authn_requests_signed": "false",
409410
"want_authn_requests_signed": "false",
411+
"want_authn_requests_only_with_valid_cert": "false",
410412
}
411413

412414

@@ -527,6 +529,16 @@ def do_idpsso_descriptor(conf, cert=None):
527529
except KeyError:
528530
setattr(idpsso, key, DEFAULTS[key])
529531

532+
for key in ["want_authn_requests_only_with_valid_cert"]:
533+
try:
534+
val = conf.getattr(key, "idp")
535+
if val is None:
536+
setattr(idpsso, key, DEFAULT["want_authn_requests_only_with_valid_cert"])
537+
else:
538+
setattr(idpsso, key, "%s" % val)
539+
except KeyError:
540+
setattr(idpsso, key, DEFAULTS[key])
541+
530542
return idpsso
531543

532544

src/saml2/request.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ def _clear(self):
3636
self.message = None
3737
self.not_on_or_after = 0
3838

39-
def _loads(self, xmldata, binding=None, origdoc=None, must=None):
39+
def _loads(self, xmldata, binding=None, origdoc=None, must=None, only_valid_cert=False):
4040
# own copy
4141
self.xmlstr = xmldata[:]
4242
logger.info("xmlstr: %s" % (self.xmlstr,))
4343
try:
44-
self.message = self.signature_check(xmldata, origdoc=origdoc, must=must)
44+
self.message = self.signature_check(xmldata, origdoc=origdoc, must=must, only_valid_cert=only_valid_cert)
4545
except TypeError:
4646
raise
4747
except Exception, excp:
@@ -84,8 +84,8 @@ def _verify(self):
8484
assert self.issue_instant_ok()
8585
return self
8686

87-
def loads(self, xmldata, binding, origdoc=None, must=None):
88-
return self._loads(xmldata, binding, origdoc, must)
87+
def loads(self, xmldata, binding, origdoc=None, must=None, only_valid_cert=False):
88+
return self._loads(xmldata, binding, origdoc, must, only_valid_cert=only_valid_cert)
8989

9090
def verify(self):
9191
try:

src/saml2/response.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,9 +553,10 @@ def condition_ok(self, lax=False):
553553
else:
554554
self.not_on_or_after = 0
555555

556-
if not for_me(conditions, self.entity_id):
557-
if not lax:
558-
raise Exception("Not for me!!!")
556+
if not self.allow_unsolicited:
557+
if not for_me(conditions, self.entity_id):
558+
if not lax:
559+
raise Exception("Not for me!!!")
559560

560561
if conditions.condition: # extra conditions
561562
for cond in conditions.condition:

src/saml2/sigver.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,9 +1006,13 @@ def verify_cert(self, cert_file):
10061006
def generate_cert(self):
10071007
return self._generate_cert
10081008

1009-
def update_cert(self, active=False):
1010-
if self._generate_cert and active:
1011-
if self._cert_handler_extra_class is not None and self._cert_handler_extra_class.use_generate_cert_func():
1009+
def update_cert(self, active=False, client_crt=None):
1010+
if (self._generate_cert and active) or client_crt is not None:
1011+
if client_crt is not None:
1012+
self._tmp_cert_str = client_crt
1013+
#No private key for signing
1014+
self._tmp_key_str = ""
1015+
elif self._cert_handler_extra_class is not None and self._cert_handler_extra_class.use_generate_cert_func():
10121016
(self._tmp_cert_str, self._tmp_key_str) = \
10131017
self._cert_handler_extra_class.generate_cert(self._cert_info, self._cert_str, self._key_str)
10141018
else:
@@ -1127,7 +1131,7 @@ def verify_signature(self, signedtext, cert_file=None, cert_type="pem",
11271131
)
11281132

11291133
def _check_signature(self, decoded_xml, item, node_name=NODE_NAME,
1130-
origdoc=None, id_attr="", must=False):
1134+
origdoc=None, id_attr="", must=False, only_valid_cert=False):
11311135
#print item
11321136
try:
11331137
issuer = item.issuer.text.strip()
@@ -1196,7 +1200,7 @@ def _check_signature(self, decoded_xml, item, node_name=NODE_NAME,
11961200
logger.error("check_sig: %s" % exc)
11971201
raise
11981202

1199-
if not verified:
1203+
if (not verified) and (not only_valid_cert):
12001204
raise SignatureError("Failed to verify signature")
12011205
else:
12021206
if not self.cert_handler.verify_cert(last_pem_file):
@@ -1211,7 +1215,7 @@ def check_signature(self, item, node_name=NODE_NAME, origdoc=None,
12111215
id_attr=id_attr, must=must)
12121216

12131217
def correctly_signed_message(self, decoded_xml, msgtype, must=False,
1214-
origdoc=None):
1218+
origdoc=None, only_valid_cert=False):
12151219
"""Check if a request is correctly signed, if we have metadata for
12161220
the entity that sent the info use that, if not use the key that are in
12171221
the message if any.
@@ -1239,12 +1243,12 @@ def correctly_signed_message(self, decoded_xml, msgtype, must=False,
12391243
return msg
12401244

12411245
return self._check_signature(decoded_xml, msg, class_name(msg),
1242-
origdoc, must=must)
1246+
origdoc, must=must, only_valid_cert=only_valid_cert)
12431247

12441248
def correctly_signed_authn_request(self, decoded_xml, must=False,
1245-
origdoc=None):
1249+
origdoc=None, only_valid_cert=False):
12461250
return self.correctly_signed_message(decoded_xml, "authn_request",
1247-
must, origdoc)
1251+
must, origdoc, only_valid_cert=only_valid_cert)
12481252

12491253
def correctly_signed_authn_query(self, decoded_xml, must=False,
12501254
origdoc=None):

0 commit comments

Comments
 (0)