|
1 | 1 | import logging |
2 | 2 |
|
| 3 | +from saml2 import time_util |
| 4 | +from saml2 import BINDING_HTTP_REDIRECT |
| 5 | +from saml2 import BINDING_HTTP_POST |
3 | 6 | from saml2.attribute_converter import to_local |
4 | | -from saml2 import time_util, BINDING_HTTP_REDIRECT |
5 | 7 | from saml2.s_utils import OtherError |
6 | 8 |
|
7 | 9 | from saml2.validate import valid_instance |
8 | 10 | from saml2.validate import NotValid |
9 | 11 | from saml2.response import IncorrectlySigned |
| 12 | +from saml2.sigver import verify_redirect_signature |
10 | 13 |
|
11 | 14 | logger = logging.getLogger(__name__) |
12 | 15 |
|
@@ -36,38 +39,84 @@ def _clear(self): |
36 | 39 | self.message = None |
37 | 40 | self.not_on_or_after = 0 |
38 | 41 |
|
39 | | - def _loads(self, xmldata, binding=None, origdoc=None, must=None, |
40 | | - only_valid_cert=False): |
41 | | - if binding == BINDING_HTTP_REDIRECT: |
42 | | - pass |
43 | | - |
| 42 | + def _loads( |
| 43 | + self, |
| 44 | + xmldata, |
| 45 | + binding=None, |
| 46 | + origdoc=None, |
| 47 | + must=None, |
| 48 | + only_valid_cert=False, |
| 49 | + relay_state=None, |
| 50 | + sigalg=None, |
| 51 | + signature=None, |
| 52 | + ): |
44 | 53 | # own copy |
45 | 54 | self.xmlstr = xmldata[:] |
46 | | - logger.debug("xmlstr: %s", self.xmlstr) |
| 55 | + logger.debug("xmlstr: %s, relay_state: %s, sigalg: %s, signature: %s", |
| 56 | + self.xmlstr, relay_state, sigalg, signature) |
| 57 | + |
| 58 | + signed_post = must and binding == BINDING_HTTP_POST |
| 59 | + signed_redirect = must and binding == BINDING_HTTP_REDIRECT |
| 60 | + incorrectly_signed = IncorrectlySigned("Request was not signed correctly") |
| 61 | + |
47 | 62 | try: |
48 | | - self.message = self.signature_check(xmldata, origdoc=origdoc, |
49 | | - must=must, |
50 | | - only_valid_cert=only_valid_cert) |
51 | | - except TypeError: |
52 | | - raise |
53 | | - except Exception as excp: |
54 | | - logger.info("EXCEPTION: %s", excp) |
| 63 | + self.message = self.signature_check( |
| 64 | + xmldata, |
| 65 | + origdoc=origdoc, |
| 66 | + must=signed_post, |
| 67 | + only_valid_cert=only_valid_cert, |
| 68 | + ) |
| 69 | + except Exception as e: |
| 70 | + self.message = None |
| 71 | + raise incorrectly_signed from e |
| 72 | + |
| 73 | + if signed_redirect: |
| 74 | + if sigalg is None or signature is None: |
| 75 | + raise incorrectly_signed |
| 76 | + |
| 77 | + _saml_msg = { |
| 78 | + "SAMLRequest": origdoc, |
| 79 | + "Signature": signature, |
| 80 | + "SigAlg": sigalg, |
| 81 | + } |
| 82 | + if relay_state is not None: |
| 83 | + _saml_msg["RelayState"] = relay_state |
| 84 | + try: |
| 85 | + sig_verified = self._do_redirect_sig_check(_saml_msg) |
| 86 | + except Exception as e: |
| 87 | + self.message = None |
| 88 | + raise incorrectly_signed from e |
| 89 | + else: |
| 90 | + if not sig_verified: |
| 91 | + self.message = None |
| 92 | + raise incorrectly_signed |
55 | 93 |
|
56 | 94 | if not self.message: |
57 | | - logger.error("Response was not correctly signed") |
58 | | - logger.info("Response: %s", xmldata) |
59 | | - raise IncorrectlySigned() |
| 95 | + logger.error("Request was not signed correctly") |
| 96 | + logger.info("Request data: %s", xmldata) |
| 97 | + raise incorrectly_signed |
60 | 98 |
|
61 | | - logger.info("request: %s", self.message) |
| 99 | + logger.info("Request message: %s", self.message) |
62 | 100 |
|
63 | 101 | try: |
64 | 102 | valid_instance(self.message) |
65 | 103 | except NotValid as exc: |
66 | | - logger.error("Not valid request: %s", exc.args[0]) |
| 104 | + logger.error("Request not valid: %s", exc.args[0]) |
67 | 105 | raise |
68 | 106 |
|
69 | 107 | return self |
70 | 108 |
|
| 109 | + def _do_redirect_sig_check(self, _saml_msg): |
| 110 | + issuer = self.message.issuer.text.strip() |
| 111 | + certs = self.sec.metadata.certs(issuer, "any", "signing") |
| 112 | + logger.debug("Certs to verify request sig: %s, _saml_msg: %s", certs, _saml_msg) |
| 113 | + verified = any( |
| 114 | + verify_redirect_signature(_saml_msg, self.sec.sec_backend, cert) |
| 115 | + for cert_name, cert in certs |
| 116 | + ) |
| 117 | + logger.info("Redirect request signature check: %s", verified) |
| 118 | + return verified |
| 119 | + |
71 | 120 | def issue_instant_ok(self): |
72 | 121 | """ Check that the request was issued at a reasonable time """ |
73 | 122 | upper = time_util.shift_time(time_util.time_in_a_while(days=1), |
@@ -97,9 +146,10 @@ def _verify(self): |
97 | 146 | return valid |
98 | 147 |
|
99 | 148 | def loads(self, xmldata, binding, origdoc=None, must=None, |
100 | | - only_valid_cert=False): |
| 149 | + only_valid_cert=False, relay_state=None, sigalg=None, signature=None): |
101 | 150 | return self._loads(xmldata, binding, origdoc, must, |
102 | | - only_valid_cert=only_valid_cert) |
| 151 | + only_valid_cert=only_valid_cert, relay_state=relay_state, |
| 152 | + sigalg=sigalg, signature=signature) |
103 | 153 |
|
104 | 154 | def verify(self): |
105 | 155 | try: |
|
0 commit comments