def test(): srvs = sp.metadata.single_sign_on_service(idp.config.entityid, BINDING_HTTP_REDIRECT) destination = srvs[0]["location"] req = sp.create_authn_request(destination, id="id1") try: key = sp.sec.key except AttributeError: key = rsa_load(sp.sec.key_file) info = http_redirect_message(req, destination, relay_state="RS", typ="SAMLRequest", sigalg=RSA_SHA1, key=key) verified_ok = False for param, val in info["headers"]: if param == "Location": _dict = parse_qs(val.split("?")[1]) _certs = idp.metadata.certs(sp.config.entityid, "any", "signing") for cert in _certs: if verify_redirect_signature(_dict, cert): verified_ok = True assert verified_ok
def test(): srvs = sp.metadata.single_sign_on_service(idp.config.entityid, BINDING_HTTP_REDIRECT) destination = srvs[0]["location"] req_id, req = sp.create_authn_request(destination, id="id1") try: key = sp.sec.key except AttributeError: key = import_rsa_key_from_file(sp.sec.key_file) info = http_redirect_message(req, destination, relay_state="RS", typ="SAMLRequest", sigalg=RSA_SHA1, key=key) verified_ok = False for param, val in info["headers"]: if param == "Location": _dict = parse_qs(val.split("?")[1]) _certs = idp.metadata.certs(sp.config.entityid, "any", "signing") for cert in _certs: if verify_redirect_signature(_dict, cert): verified_ok = True assert verified_ok
def test(): with closing(Server(config_file=dotname("idp_all_conf"))) as idp: conf = SPConfig() conf.load_file(dotname("servera_conf")) sp = Saml2Client(conf) srvs = sp.metadata.single_sign_on_service(idp.config.entityid, BINDING_HTTP_REDIRECT) destination = srvs[0]["location"] req_id, req = sp.create_authn_request(destination, id="id1") try: key = sp.sec.key except AttributeError: key = import_rsa_key_from_file(sp.sec.key_file) info = http_redirect_message(req, destination, relay_state="RS", typ="SAMLRequest", sigalg=SIG_RSA_SHA1, key=key) verified_ok = False for param, val in info["headers"]: if param == "Location": _dict = parse_qs(val.split("?")[1]) _certs = idp.metadata.certs(sp.config.entityid, "any", "signing") for cert in _certs: if verify_redirect_signature(_dict, cert): verified_ok = True assert verified_ok
def test(): with closing(Server(config_file=dotname("idp_all_conf"))) as idp: conf = SPConfig() conf.load_file(dotname("servera_conf")) sp = Saml2Client(conf) srvs = sp.metadata.single_sign_on_service(idp.config.entityid, BINDING_HTTP_REDIRECT) destination = srvs[0]["location"] req_id, req = sp.create_authn_request(destination, id="id1") info = http_redirect_message( req, destination, relay_state="RS", typ="SAMLRequest", sigalg=SIG_RSA_SHA1, sign=True, backend=sp.sec.sec_backend, ) verified_ok = False for param, val in info["headers"]: if param == "Location": _dict = parse_qs(val.split("?")[1]) _certs = idp.metadata.certs(sp.config.entityid, "any", "signing") for cert in _certs: if verify_redirect_signature(list_values2simpletons(_dict), sp.sec.sec_backend, cert[1]): verified_ok = True assert verified_ok
def test_signed_redirect(self): msg_str = "%s" % self.client.create_authn_request( "http://localhost:8088/sso", message_id="id1")[1] key = self.client.signkey info = self.client.apply_binding(BINDING_HTTP_REDIRECT, msg_str, destination="", relay_state="relay2", sigalg=SIG_RSA_SHA256, key=key) loc = info["headers"][0][1] qs = urlparse.parse_qs(loc[1:]) assert _leq(qs.keys(), ['SigAlg', 'SAMLRequest', 'RelayState', 'Signature']) assert verify_redirect_signature(list_values2simpletons(qs), sigkey=key) res = self.server.parse_authn_request(qs["SAMLRequest"][0], BINDING_HTTP_REDIRECT) print res
def _do_redirect_sig_check(self, _saml_msg): issuer = self.message.issuer.text.strip() certs = self.sec.metadata.certs(issuer, "any", "signing") logger.debug("Certs to verify request sig: %s, _saml_msg: %s", certs, _saml_msg) verified = any( verify_redirect_signature(_saml_msg, self.sec.sec_backend, cert) for cert_name, cert in certs ) logger.info("Redirect request signature check: %s", verified) return verified
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") _info = self.unpack_redirect() cert_str = None try: _key = _info["key"] _info = self.idphandler.idp_server.ticket[_key] self.req_info = _info["req_info"] del self.idphandler.idp_server.ticket[_key] except KeyError: if self.idphandler.copy_sp_cert: with lock: self.req_info = self.idphandler.idp_server.parse_authn_request( _info["SAMLRequest"], BINDING_HTTP_REDIRECT) cert_str = self.idphandler.idp_server.getvalid_certificate_str( ) else: self.req_info = self.idphandler.idp_server.parse_authn_request( _info["SAMLRequest"], BINDING_HTTP_REDIRECT) _req = self.req_info.message if "SigAlg" in _info and "Signature" in _info: # Signed request issuer = _req.issuer.text _certs = self.idphandler.idp_server.metadata.certs( issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(_info, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) _encrypt_cert = None if self.idphandler.copy_sp_key: _encrypt_cert = encrypt_cert_from_item(self.req_info.message) if self.user: if _req.force_authn: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT) else: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT)
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") saml_msg = self.unpack_redirect() try: _key = saml_msg["key"] saml_msg = IDP.ticket[_key] self.req_info = saml_msg["req_info"] del IDP.ticket[_key] except KeyError: try: self.req_info = IDP.parse_authn_request(saml_msg["SAMLRequest"], BINDING_HTTP_REDIRECT) except KeyError: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if not self.req_info: resp = BadRequest("Message parsing failed") return resp(self.environ, self.start_response) _req = self.req_info.message if "SigAlg" in saml_msg and "Signature" in saml_msg: # Signed request issuer = _req.issuer.text _certs = IDP.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(saml_msg, IDP.sec.sec_backend, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if self.user: saml_msg["req_info"] = self.req_info if _req.force_authn is not None and \ _req.force_authn.lower() == 'true': key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT) else: saml_msg["req_info"] = self.req_info key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT)
def _do_redirect_sig_check(self, _saml_msg): _issuer = self.message.issuer.text.strip() _certs = self.sec.metadata.certs(_issuer, "any", "signing") logger.debug("Certs: %s, _saml_msg: %s", _certs, _saml_msg) _verified_ok = False for cert in _certs: if verify_redirect_signature(_saml_msg, self.sec.sec_backend, cert): _verified_ok = True break logger.info("Redirect request signature check: %s", _verified_ok) return _verified_ok
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") saml_msg = self.unpack_redirect() try: _key = saml_msg["key"] saml_msg = IDP.ticket[_key] self.req_info = saml_msg["req_info"] del IDP.ticket[_key] except KeyError: try: self.req_info = IDP.parse_authn_request( saml_msg["SAMLRequest"], BINDING_HTTP_REDIRECT) except KeyError: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if not self.req_info: resp = BadRequest("Message parsing failed") return resp(self.environ, self.start_response) _req = self.req_info.message if "SigAlg" in saml_msg and "Signature" in saml_msg: # Signed request issuer = _req.issuer.text _certs = IDP.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(saml_msg, IDP.sec.sec_backend, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if self.user: saml_msg["req_info"] = self.req_info if _req.force_authn is not None and \ _req.force_authn.lower() == 'true': key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT) else: saml_msg["req_info"] = self.req_info key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT)
def single_sign_on_service(self): """ Process Http-Redirect or Http-POST request :param request: Flask request object """ self.app.logger.info("Http-Redirect") # Unpack parameters saml_msg = self.unpack_args(request.args) try: _key = session['request_key'] req_info = self.ticket[_key] except KeyError as e: try: binding = self._get_binding('single_sign_on_service', request) # Parse AuthnRequest req_info = self.server.parse_authn_request( saml_msg["SAMLRequest"], binding) authn_req = req_info.message except KeyError as err: self.app.logger.debug(str(err)) self._raise_error('Parametro SAMLRequest assente.') if not req_info: self._raise_error('Processo di parsing del messaggio fallito.') self.app.logger.debug('AuthnRequest: {}'.format(authn_req)) # Check if it is signed if "SigAlg" in saml_msg and "Signature" in saml_msg: # Signed request self.app.logger.debug('Messaggio SAML firmato.') issuer_name = authn_req.issuer.text _certs = self.server.metadata.certs(issuer_name, "any", "signing") verified_ok = False for cert in _certs: self.app.logger.debug('security backend: {}'.format( self.server.sec.sec_backend.__class__.__name__)) # Check signature if verify_redirect_signature(saml_msg, self.server.sec.sec_backend, cert): verified_ok = True break if not verified_ok: self._raise_error( 'Verifica della firma del messaggio fallita.') # Perform login key = self._store_request(req_info) relay_state = saml_msg.get('RelayState', '') session['request_key'] = key session['relay_state'] = relay_state return redirect(url_for('login'))
def verify_signature(url, cert='/home/ashima/Rackspace/astra-service-providers/dev_ssl/sso_cert'): try: cert = open(cert, 'r').read().splitlines(True) # cert is path to certfile cert = ''.join(cert[1:-1]) # Remove begin cert and end cert lines except IOError: pass # cert is the certificate else: print(cert) crypto = RSACrypto('dummy key') # The key is not required in this case saml_msg = dict(parse_qsl(urlsplit(url).query)) return verify_redirect_signature(saml_msg, crypto, cert=cert)
def redirect(self): """ This is the HTTP-redirect endpoint for SSO """ logger.info("--- In SSO Redirect ---") _info = self.unpack_redirect() cert_str = None try: _key = _info["key"] _info = self.idphandler.idp_server.ticket[_key] self.req_info = _info["req_info"] del self.idphandler.idp_server.ticket[_key] except KeyError: if self.idphandler.copy_sp_cert: with lock: self.req_info = self.idphandler.idp_server.parse_authn_request(_info["SAMLRequest"], BINDING_HTTP_REDIRECT) cert_str = self.idphandler.idp_server.getvalid_certificate_str() else: self.req_info = self.idphandler.idp_server.parse_authn_request(_info["SAMLRequest"], BINDING_HTTP_REDIRECT) _req = self.req_info.message if "SigAlg" in _info and "Signature" in _info: # Signed request issuer = _req.issuer.text _certs = self.idphandler.idp_server.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(_info, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) _encrypt_cert = None if self.idphandler.copy_sp_key: _encrypt_cert = encrypt_cert_from_item(self.req_info.message) if self.user: if _req.force_authn: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT) else: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT)
def verify_signature( url, cert='/home/ashima/Rackspace/astra-service-providers/dev_ssl/sso_cert' ): try: cert = open(cert, 'r').read().splitlines(True) # cert is path to certfile cert = ''.join(cert[1:-1]) # Remove begin cert and end cert lines except IOError: pass # cert is the certificate else: print(cert) crypto = RSACrypto('dummy key') # The key is not required in this case saml_msg = dict(parse_qsl(urlsplit(url).query)) return verify_redirect_signature(saml_msg, crypto, cert=cert)
def _func(self, conv): req = conv.events.last_item(EV_RESPONSE) res = TestResult(self.cid) # First, was the whole message signed if 'SigAlg' in req: if not verify_redirect_signature( req['SAMLRequest'], conv.entity.sec): res.message = "Was not able to verify Redirect message " \ "signature" res.status = CRITICAL # Secondly, was the XML doc signed req = conv.events.get_message(EV_PROTOCOL_REQUEST, request.AuthnRequest) if req.message.signature is None: res.message = 'Missing response signature' res.status = CRITICAL return res
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") _info = self.unpack_redirect() try: _key = _info["key"] _info = self.idp.ticket[_key] self.req_info = _info["req_info"] del self.idp.ticket[_key] except KeyError: self.req_info = self.idp.parse_authn_request(_info["SAMLRequest"], BINDING_HTTP_REDIRECT) _req = self.req_info.message if "SigAlg" in _info and "Signature" in _info: # Signed request issuer = _req.issuer.text _certs = self.idp.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature( _info, self.idp.sec.sec_backend, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if self.user: if _req.force_authn: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(_info, BINDING_HTTP_REDIRECT) else: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(_info, BINDING_HTTP_REDIRECT)
def _verify_redirect(self, saml_msg, issuer_name): """ Verify Http-Redirect signature :param saml_msg: request parameters :param issuer_name: issuer name (Service Provider) """ if "SigAlg" in saml_msg and "Signature" in saml_msg: # Signed request self.app.logger.debug('Messaggio SAML firmato.') _sig_alg = saml_msg['SigAlg'] if _sig_alg not in ALLOWED_SIG_ALGS: self._raise_error( 'L\'Algoritmo {} non è supportato.'.format(_sig_alg)) try: _certs = self.server.metadata.certs(issuer_name, "any", "signing") except KeyError: self._raise_error( 'entity ID {} non registrato, impossibile ricavare'\ ' un certificato valido.'.format(issuer_name) ) verified_ok = False for cert in _certs: self.app.logger.debug('security backend: {}'.format( self.server.sec.sec_backend.__class__.__name__)) # Check signature if verify_redirect_signature(saml_msg, self.server.sec.sec_backend, cert): verified_ok = True break if not verified_ok: self._raise_error( 'Verifica della firma del messaggio fallita.') else: self._raise_error( 'I parametri Signature e SigAlg sono entrambi'\ ' necessari per le richieste di tipo HTTP-REDIRECT' )
def test_signed_redirect(self): msg_str = "%s" % self.client.create_authn_request( "http://localhost:8088/sso", message_id="id1")[1] key = self.client.signkey info = self.client.apply_binding( BINDING_HTTP_REDIRECT, msg_str, destination="", relay_state="relay2", sigalg=SIG_RSA_SHA256, key=key) loc = info["headers"][0][1] qs = urlparse.parse_qs(loc[1:]) assert _leq(qs.keys(), ['SigAlg', 'SAMLRequest', 'RelayState', 'Signature']) assert verify_redirect_signature(list_values2simpletons(qs), sigkey=key) res = self.server.parse_authn_request(qs["SAMLRequest"][0], BINDING_HTTP_REDIRECT) print res
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") _info = self._authn(self.unpack_redirect()) if isinstance(_info, basestring): return self.not_authn(_info) if "SigAlg" in _info and "Signature" in _info: # Signed request self.req_info = IDP.parse_authn_request(_info["SAMLRequest"], BINDING_HTTP_REDIRECT) issuer = self.req_info.message.issuer.text _certs = IDP.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(_info, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) return self.operation(_info, BINDING_HTTP_REDIRECT)
def _parse_SAMLRequest(self, info, binding): """ Parse a SAMLRequest query parameter (base64 encoded) into an AuthnRequest instance. If the SAMLRequest is signed, the signature is validated and a BadRequest() returned on failure. :param info: dict with keys 'SAMLRequest' and possibly 'SigAlg' and 'Signature' :param binding: SAML binding :returns: pysaml2 AuthnRequest information :raise: BadRequest if request signature validation fails :type info: dict :type binding: string :rtype: AuthnRequest """ # self.logger.debug("Parsing SAML request : {!r}".format(info["SAMLRequest"])) try: _req_info = self.IDP.parse_authn_request(info['SAMLRequest'], binding) except UnravelError as exc: self.logger.info('Failed parsing SAML request ({!s} bytes)'.format(len(info['SAMLRequest']))) self.logger.debug('Failed parsing SAML request:\n{!s}\nException {!s}'.format(info['SAMLRequest'], exc)) raise eduid_idp.error.BadRequest('No valid SAMLRequest found', logger = self.logger) if not _req_info: # Either there was no request, or pysaml2 found it to be unacceptable. # For example, the IssueInstant might have been out of bounds. self.logger.debug("No valid SAMLRequest returned by pysaml2") raise eduid_idp.error.BadRequest("No valid SAMLRequest found", logger = self.logger) assert isinstance(_req_info, AuthnRequest) # Only perform expensive parse/pretty-print if debugging if self.config.debug: xmlstr = eduid_idp.util.maybe_xml_to_string(_req_info.message) self.logger.debug("Decoded SAMLRequest into AuthnRequest {!r} :\n\n{!s}\n\n".format( _req_info.message, xmlstr)) try: # XXX Temporary debug logging clause. This whole try/except can be removed in the next release. self.logger.debug("Verify request signatures: {!r}".format(self.config.verify_request_signatures)) except AttributeError: self.logger.debug("FAILED logging verify request signatures") if "SigAlg" in info and "Signature" in info: # Signed request issuer = _req_info.message.issuer.text _certs = self.IDP.metadata.certs(issuer, "any", "signing") if self.config.verify_request_signatures: verified_ok = False for cert in _certs: if verify_redirect_signature(info, cert): verified_ok = True break if not verified_ok: _key = self._cache.key(info["SAMLRequest"]) self.logger.info("{!s}: SAML request signature verification failure".format(_key)) raise eduid_idp.error.BadRequest("SAML request signature verification failure", logger = self.logger) else: self.logger.debug("Ignoring existing request signature, verify_request_signature is False") else: # XXX check if metadata says request should be signed ??? # Leif says requests are typically not signed, and that verifying signatures # on SAML requests is considered a possible DoS attack vector, so it is typically # not done. # XXX implement configuration flag to disable signature verification self.logger.debug("No signature in SAMLRequest") return _req_info
def get(self, request, *args, **kwargs): binding = request.session.get('Binding', BINDING_HTTP_POST) # Parse incoming request try: req_info = self.IDP.parse_authn_request( request.session['SAMLRequest'], binding) except Exception as excp: logger.error('error %s', excp) return self.handle_error(request, exception=excp) # Signed request for HTTP-REDIRECT if "SigAlg" in request.session and "Signature" in request.session: _certs = self.IDP.metadata.certs(req_info.message.issuer.text, "any", "signing") verified_ok = False logger.debug('should check signature') logger.debug('_certs %s', _certs) logger.debug('req_info %s', req_info) logger.debug('self.IDP.sec.sec_backend %s', verify_redirect_signature) d = dict(SAMLRequest=request.session['SAMLRequest'], SigAlg=request.session['SigAlg'], Signature=request.session['Signature']) for cert in _certs: # TODO implement if verify_redirect_signature(d, self.IDP.sec.sec_backend, cert): verified_ok = True break if not verified_ok: return self.handle_error( request, extra_message="Message signature verification failure", status=400) # Gather response arguments try: resp_args = self.IDP.response_args(req_info.message) except (UnknownPrincipal, UnsupportedBinding) as excp: return self.handle_error(request, exception=excp, status=400) try: sp_config = settings.SAML_IDP_SPCONFIG[resp_args['sp_entity_id']] except Exception: return self.handle_error( request, exception=ImproperlyConfigured( "No config for SP %s defined in SAML_IDP_SPCONFIG" % resp_args['sp_entity_id']), status=400) processor = self.get_processor(resp_args['sp_entity_id'], sp_config) # Check if user has access to the service of this SP if not processor.has_access(request): return self.handle_error( request, exception=PermissionDenied( "You do not have access to this resource"), status=403) identity = self.get_identity(processor, request.user, sp_config) req_authn_context = req_info.message.requested_authn_context or PASSWORD AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") user_id = processor.get_user_id(request.user) # Construct SamlResponse message try: authn_resp = self.IDP.create_authn_response( identity=identity, userid=user_id, name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=resp_args['sp_entity_id'], text=user_id), authn=AUTHN_BROKER.get_authn_by_accr(req_authn_context), sign_response=self.IDP.config.getattr("sign_response", "idp") or False, sign_assertion=self.IDP.config.getattr("sign_assertion", "idp") or False, **resp_args) except Exception as excp: return self.handle_error(request, exception=excp, status=500) http_args = self.IDP.apply_binding( binding=resp_args['binding'], msg_str="%s" % authn_resp, destination=resp_args['destination'], relay_state=request.session['RelayState'], response=True) logger.debug('http args are: %s' % http_args) return self.render_response(request, processor, http_args)