/// <summary> /// Decrypts an encrypted assertion, and sends the result to the HandleAssertion method. /// </summary> private void HandleEncryptedAssertion(HttpContext context, XmlElement elem) { Trace.TraceMethodCalled(GetType(), "HandleEncryptedAssertion()"); Saml20EncryptedAssertion decryptedAssertion = GetDecryptedAssertion(elem); HandleAssertion(context, decryptedAssertion.Assertion.DocumentElement); }
/// <summary> /// Send an authentication request to the IDP. /// </summary> private void SendRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "SendRequest()"); // See if the "ReturnUrl" - parameter is set. string returnUrl = context.Request.QueryString["ReturnUrl"]; if (!string.IsNullOrEmpty(returnUrl)) { context.Session["RedirectUrl"] = returnUrl; } IDPEndPoint idpEndpoint = RetrieveIDP(context); if (idpEndpoint == null) { //Display a page to the user where she can pick the IDP SelectSaml20IDP page = new SelectSaml20IDP(); page.ProcessRequest(context); return; } Saml20AuthnRequest authnRequest = Saml20AuthnRequest.GetDefault(); TransferClient(idpEndpoint, authnRequest, context); }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> private void HandleAssertion(HttpContext context, XmlElement elem) { Trace.TraceMethodCalled(GetType(), "HandleAssertion"); string issuer = GetIssuer(elem); IDPEndPoint endp = RetrieveIDPConfiguration(issuer); AuditLogging.IdpId = endp.Id; PreHandleAssertion(context, elem, endp); bool quirksMode = false; if (endp != null) { quirksMode = endp.QuirksMode; } //Saml20Assertion assertion = new Saml20Assertion(elem, null, quirksMode); Saml20Assertion assertion = new Saml20Assertion(elem, null, AssertionProfile.Core, quirksMode); if (endp == null || endp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Unknown login IDP, assertion: " + elem); HandleError(context, Resources.UnknownLoginIDP); return; } if (!endp.OmitAssertionSignatureCheck) { if (!assertion.CheckSignature(GetTrustedSigners(endp.metadata.GetKeys(KeyTypes.signing), endp))) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Invalid signature, assertion: " + elem); HandleError(context, Resources.SignatureInvalid); return; } } if (assertion.IsExpired()) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion expired, assertion: " + elem.OuterXml); HandleError(context, Resources.AssertionExpired); return; } CheckConditions(context, assertion); AuditLogging.AssertionId = assertion.Id; AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion validated succesfully"); DoLogin(context, assertion); }
/// <summary> /// Handles a request. /// </summary> /// <param name="context">The context.</param> protected override void Handle(HttpContext context) { Trace.TraceMethodCalled(GetType(), "Handle()"); try { //Some IdP's are known to fail to set an actual value in the SOAPAction header //so we just check for the existence of the header field. if (Array.Exists(context.Request.Headers.AllKeys, delegate(string s) { return s == SOAPConstants.SOAPAction; })) { HandleSOAP(context, context.Request.InputStream); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { HandleArtifact(context); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLResponse"])) { HandleResponse(context); } else if(!string.IsNullOrEmpty(context.Request.Params["SAMLRequest"])) { HandleRequest(context); } else { IDPEndPoint idpEndpoint = null; //context.Session[IDPLoginSessionKey] may be null if IIS has been restarted if (context.Session[IDPSessionIdKey] != null) { idpEndpoint = RetrieveIDPConfiguration(context.Session[IDPLoginSessionKey].ToString()); } if (idpEndpoint == null) { context.User = null; FormsAuthentication.SignOut(); HandleError(context, Resources.UnknownLoginIDP); } TransferClient(idpEndpoint, context); } }catch(Exception e) { //ThreadAbortException is thrown by response.Redirect so don't worry about it if(e is ThreadAbortException) throw; HandleError(context, e.Message); } }
private void DoLogout(HttpContext context, bool IdPInitiated) { foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LogoutAction()"); action.LogoutAction(this, context, IdPInitiated); Trace.TraceMethodDone(action.GetType(), "LogoutAction()"); } }
/// <summary> /// Is called before the assertion is made into a strongly typed representation /// </summary> /// <param name="context">The httpcontext.</param> /// <param name="elem">The assertion element.</param> /// <param name="endpoint">The endpoint.</param> protected virtual void PreHandleAssertion(HttpContext context, XmlElement elem, IDPEndPoint endpoint) { Trace.TraceMethodCalled(GetType(), "PreHandleAssertion"); if (endpoint != null && endpoint.SLOEndpoint != null && !String.IsNullOrEmpty(endpoint.SLOEndpoint.IdpTokenAccessor)) { ISaml20IdpTokenAccessor idpTokenAccessor = Activator.CreateInstance(Type.GetType(endpoint.SLOEndpoint.IdpTokenAccessor, false)) as ISaml20IdpTokenAccessor; if (idpTokenAccessor != null) { idpTokenAccessor.ReadToken(elem); } } Trace.TraceMethodDone(GetType(), "PreHandleAssertion"); }
private void DoLogin(HttpContext context, Saml20Assertion assertion) { //User is now logged in at IDP specified in tmp context.Session[IDPLoginSessionKey] = context.Session[IDPTempSessionKey]; context.Session[IDPSessionIdKey] = assertion.SessionIndex; context.Session[IDPNameIdFormat] = assertion.Subject.Format; context.Session[IDPNameId] = assertion.Subject.Value; if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.Login, assertion.Subject.Value, assertion.SessionIndex, assertion.Subject.Format)); } string inResponseTo = "(unknown)"; if (assertion.GetSubjectConfirmationData() != null && assertion.GetSubjectConfirmationData().InResponseTo != null) { inResponseTo = assertion.GetSubjectConfirmationData().InResponseTo; } string assuranceLevel = "(unknown)"; foreach (var attribute in assertion.Attributes) { if (attribute.Name == "dk:gov:saml:attribute:AssuranceLevel" && attribute.AttributeValue != null && attribute.AttributeValue.Length > 0) { assuranceLevel = attribute.AttributeValue[0]; } } AuditLogging.logEntry(Direction.IN, Operation.LOGIN, string.Format("Subject: {0} NameIDFormat: {1} Level of authentication: {2} Session timeout in minutes: {3}", assertion.Subject.Value, assertion.Subject.Format, assuranceLevel, HttpContext.Current.Session.Timeout)); foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LoginAction()"); action.LoginAction(this, context, assertion); Trace.TraceMethodDone(action.GetType(), "LoginAction()"); } }
/// <summary> /// Resolves an artifact. /// </summary> /// <returns>A stream containing the artifact response from the IdP</returns> public Stream ResolveArtifact() { Trace.TraceMethodCalled(GetType(), "ResolveArtifact()"); string artifact = _context.Request.Params["SAMLart"]; IDPEndPoint idpEndPoint = DetermineIdp(artifact); if (idpEndPoint == null) { throw new InvalidOperationException("Received artifact from unknown IDP."); } ushort endpointIndex = ArtifactUtil.GetEndpointIndex(artifact); string endpointUrl = idpEndPoint.metadata.GetARSEndpoint(endpointIndex); Saml20ArtifactResolve resolve = Saml20ArtifactResolve.GetDefault(); resolve.Artifact = artifact; XmlDocument doc = resolve.GetXml(); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } XmlSignatureUtils.SignDocument(doc, resolve.ID); string artifactResolveString = doc.OuterXml; if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.ResolveArtifact, artifact, idpEndPoint.Id, endpointIndex, endpointUrl, artifactResolveString)); } return(GetResponse(endpointUrl, artifactResolveString, idpEndPoint.ArtifactResolution)); }
/// <summary> /// Handles a request. /// </summary> /// <param name="context">The context.</param> protected override void Handle(HttpContext context) { Trace.TraceMethodCalled(GetType(), "Handle()"); //Some IdP's are known to fail to set an actual value in the SOAPAction header //so we just check for the existence of the header field. if (Array.Exists(context.Request.Headers.AllKeys, delegate(string s) { return(s == SOAPConstants.SOAPAction); })) { HandleSOAP(context, context.Request.InputStream); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { HandleArtifact(context); } if (!string.IsNullOrEmpty(context.Request.Params["SamlResponse"])) { HandleResponse(context); } else { if (SAML20FederationConfig.GetConfig().CommonDomain.Enabled&& context.Request.QueryString["r"] == null && context.Request.Params["cidp"] == null) { AuditLogging.logEntry(Direction.OUT, Operation.DISCOVER, "Redirecting to Common Domain for IDP discovery"); context.Response.Redirect(SAML20FederationConfig.GetConfig().CommonDomain.LocalReaderEndpoint); } else { AuditLogging.logEntry(Direction.IN, Operation.ACCESS, "User accessing resource: " + context.Request.RawUrl + " without authentication."); SendRequest(context); } } }
private void HandleRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleRequest()"); //Fetch config object SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); LogoutRequest logoutRequest = null; IDPEndPoint endpoint = null; string message = string.Empty; //Build the response object var response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; response.StatusCode = Saml20Constants.StatusCodes.Success; // Default success. Is overwritten if something fails. if(context.Request.RequestType == "GET") // HTTP Redirect binding { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, string.Format("Binding: redirect, Signature algorithm: {0} Signature: {1}, Message: {2}", parser.SignatureAlgorithm, parser.Signature, parser.Message)); if (!parser.IsSigned) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP: " + logoutRequest.Issuer.Value); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } Saml20MetadataDocument metadata = endpoint.metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature redirect-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; } else if (context.Request.RequestType == "POST") // HTTP Post binding { HttpPostBindingParser parser = new HttpPostBindingParser(context); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Binding: POST, Message: " + parser.Message); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } logoutRequest = parser.LogoutRequest; endpoint = config.FindEndPoint(logoutRequest.Issuer.Value); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); // Not able to return a response as we do not know the IdP. HandleError(context, "Cannot find metadata for IdP " + logoutRequest.Issuer.Value); return; } Saml20MetadataDocument metadata = endpoint.metadata; // handle a logout-request if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature post-binding, msg: " + parser.Message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } message = parser.Message; }else { //Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS // Not able to return a response as we do not understand the request. HandleError(context, Resources.UnsupportedRequestTypeFormat(context.Request.RequestType)); } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); // Check that idp in session and request matches. string idpRequest = logoutRequest.Issuer.Value; if (!context.Session.IsNewSession) { object idpSession = context.Session[IDPLoginSessionKey]; if (idpSession != null && idpSession.ToString() != idpRequest) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, Resources.IdPMismatchBetweenRequestAndSessionFormat(idpSession, idpRequest), message); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } } else { // All other status codes than Success results in the IdP throwing an error page. Therefore we return default Success even if we do not have a session. AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Session does not exist. Continues the redirect logout procedure with status code success." + idpRequest, message); } // Only logout if request is valid and we are working on an existing Session. if (Saml20Constants.StatusCodes.Success == response.StatusCode && !context.Session.IsNewSession) { // Execute all actions that the service provider has configured DoLogout(context, true); } // Update the response object with informations that first is available when request has been parsed. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); response.Destination = destination.Url; response.InResponseTo = logoutRequest.ID; //Respond using redirect binding if(destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.RelayState = context.Request.Params["RelayState"]; builder.Response = response.GetXml().OuterXml; builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; string s = destination.Url + "?" + builder.ToQuery(); context.Response.Redirect(s, true); return; } //Respond using post binding if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); builder.Action = SAMLAction.SAMLResponse; XmlDocument responseDocument = response.GetXml(); XmlSignatureUtils.SignDocument(responseDocument, response.ID); builder.Response = responseDocument.OuterXml; builder.RelayState = context.Request.Params["RelayState"]; builder.GetPage().ProcessRequest(context); return; } }
private void HandleResponse(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleResponse()"); string message = string.Empty; if(context.Request.RequestType == "GET") { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); LogoutResponse response = Serialization.DeserializeFromXmlString<LogoutResponse>(parser.Message); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Binding: redirect, Signature algorithm: {0} Signature: {1}, Message: {2}", parser.SignatureAlgorithm, parser.Signature, parser.Message)); IDPEndPoint idp = RetrieveIDPConfiguration(response.Issuer.Value); AuditLogging.IdpId = idp.Id; AuditLogging.AssertionId = response.ID; if (idp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("No IDP metadata, unknown IDP, response: {0}", parser.Message)); HandleError(context, Resources.UnknownIDP); return; } if (!parser.VerifySignature(idp.metadata.Keys)) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Invalid signature in redirect-binding, response: {0}", parser.Message)); HandleError(context, Resources.SignatureInvalid); return; } message = parser.Message; }else if(context.Request.RequestType == "POST") { HttpPostBindingParser parser = new HttpPostBindingParser(context); AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, "Binding: POST, Message: " + parser.Message); LogoutResponse response = Serialization.DeserializeFromXmlString<LogoutResponse>(parser.Message); IDPEndPoint idp = RetrieveIDPConfiguration(response.Issuer.Value); if (idp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("No IDP metadata, unknown IDP, response: {0}", parser.Message)); HandleError(context, Resources.UnknownIDP); return; } if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Signature not present, response: {0}", parser.Message)); HandleError(context, Resources.SignatureNotPresent); } // signature on final message in logout if (!parser.CheckSignature(idp.metadata.Keys)) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Invalid signature in post-binding, response: {0}", parser.Message)); HandleError(context, Resources.SignatureInvalid); } message = parser.Message; }else { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Unsupported request type format, type: {0}", context.Request.RequestType)); HandleError(context, Resources.UnsupportedRequestTypeFormat(context.Request.RequestType)); } XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = true; doc.LoadXml(message); XmlElement statElem = (XmlElement)doc.GetElementsByTagName(Status.ELEMENT_NAME, Saml20Constants.PROTOCOL)[0]; Status status = Serialization.DeserializeFromXmlString<Status>(statElem.OuterXml); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, string.Format("Unexpected status code: {0}, msg: {1}", status.StatusCode.Value, message)); HandleError(context, status); return; } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTRESPONSE, "Assertion validated succesfully"); //Log the user out locally DoLogout(context); }
private void TransferClient(IDPEndPoint endpoint, HttpContext context) { Trace.TraceMethodCalled(GetType(), "TransferClient()"); Saml20LogoutRequest request = Saml20LogoutRequest.GetDefault(); AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = endpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); request.Destination = destination.Url; string nameIdFormat = context.Session[IDPNameIdFormat].ToString(); request.SubjectToLogOut.Format = nameIdFormat; if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); XmlDocument requestDocument = request.GetXml(); XmlSignatureUtils.SignDocument(requestDocument, request.ID); builder.Request = requestDocument.OuterXml; if(Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "POST", endpoint.Id, requestDocument.OuterXml)); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Binding: POST"); builder.GetPage().ProcessRequest(context); context.Response.End(); return; } if(destination.Binding == SAMLBinding.REDIRECT) { HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey; request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); builder.Request = request.GetXml().OuterXml; string redirectUrl = destination.Url + "?" + builder.ToQuery(); if (Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "REDIRECT", endpoint.Id, redirectUrl)); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Binding: Redirect"); context.Response.Redirect(redirectUrl, true); return; } if(destination.Binding == SAMLBinding.ARTIFACT) { if (Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendLogoutRequest, "ARTIFACT", endpoint.Id, string.Empty)); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = context.Session[IDPNameId].ToString(); request.SessionIndex = context.Session[IDPSessionIdKey].ToString(); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Method: Artifact"); builder.RedirectFromLogout(destination, request, Guid.NewGuid().ToString("N")); } HandleError(context, Resources.BindingError); }
private void HandleSOAP(HttpContext context, Stream inputStream) { Trace.TraceMethodCalled(GetType(), "HandleSOAP"); HttpArtifactBindingParser parser = new HttpArtifactBindingParser(inputStream); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); IDPEndPoint idp = RetrieveIDPConfiguration(parser.Issuer); AuditLogging.IdpId = idp.Id; if (parser.IsArtifactResolve()) { Trace.TraceData(TraceEventType.Information, Tracing.ArtifactResolveIn); if (!parser.CheckSamlMessageSignature(idp.metadata.Keys)) { HandleError(context, "Invalid Saml message signature"); AuditLogging.logEntry(Direction.UNDEFINED, Operation.ARTIFACTRESOLVE, "Signature could not be verified", parser.SamlMessage); } AuditLogging.AssertionId = parser.ArtifactResolve.ID; AuditLogging.logEntry(Direction.IN, Operation.ARTIFACTRESOLVE, "", parser.SamlMessage); builder.RespondToArtifactResolve(parser.ArtifactResolve); } else if (parser.IsArtifactResponse()) { Trace.TraceData(TraceEventType.Information, Tracing.ArtifactResponseIn); Status status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { AuditLogging.logEntry(Direction.UNDEFINED, Operation.ARTIFACTRESOLVE, string.Format("Unexpected status code for artifact response: {0}, expected 'Success', msg: {1}", status.StatusCode.Value, parser.SamlMessage)); HandleError(context, status); return; } if (parser.ArtifactResponse.Any.LocalName == LogoutRequest.ELEMENT_NAME) { if(Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.LogoutRequest, parser.ArtifactResponse.Any.OuterXml)); //Send logoutresponse via artifact Saml20LogoutResponse response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; LogoutRequest req = Serialization.DeserializeFromXmlString<LogoutRequest>(parser.ArtifactResponse.Any.OuterXml); response.StatusCode = Saml20Constants.StatusCodes.Success; response.InResponseTo = req.ID; IDPEndPoint endpoint = RetrieveIDPConfiguration(context.Session[IDPLoginSessionKey].ToString()); IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, endpoint.SLOEndpoint, endpoint.metadata.SLOEndpoints()); builder.RedirectFromLogout(destination, response); }else if(parser.ArtifactResponse.Any.LocalName == LogoutResponse.ELEMENT_NAME) { DoLogout(context); } else { AuditLogging.logEntry(Direction.UNDEFINED, Operation.ARTIFACTRESOLVE, string.Format("Unsupported payload message in ArtifactResponse: {0}, msg: {1}", parser.ArtifactResponse.Any.LocalName, parser.SamlMessage)); HandleError(context, string.Format("Unsupported payload message in ArtifactResponse: {0}", parser.ArtifactResponse.Any.LocalName)); } } else if(parser.IsLogoutReqest()) { if (Trace.ShouldTrace(TraceEventType.Information)) Trace.TraceData(TraceEventType.Information, string.Format(Tracing.LogoutRequest, parser.SamlMessage.OuterXml)); LogoutRequest req = parser.LogoutRequest; //Build the response object Saml20LogoutResponse response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; //response.Destination = destination.Url; response.StatusCode = Saml20Constants.StatusCodes.Success; response.InResponseTo = req.ID; XmlDocument doc = response.GetXml(); XmlSignatureUtils.SignDocument(doc, response.ID); if (doc.FirstChild is XmlDeclaration) doc.RemoveChild(doc.FirstChild); builder.SendResponseMessage(doc.OuterXml); } else { Status s = parser.GetStatus(); if (s != null) { HandleError(context, s); } else { AuditLogging.logEntry(Direction.UNDEFINED, Operation.ARTIFACTRESOLVE, string.Format("Unsupported SamlMessage element: {0}, msg: {1}", parser.SamlMessageName, parser.SamlMessage)); HandleError(context, string.Format("Unsupported SamlMessage element: {0}", parser.SamlMessageName)); } } }
/// <summary> /// Performs the attribute query against the specified IdP endpoint and adds the resulting attributes to Saml20Identity.Current. /// </summary> /// <param name="context">The http context.</param> /// <param name="endPoint">The IdP to perform the query against.</param> /// <param name="nameIdFormat">The nameid format.</param> public void PerformQuery(HttpContext context, IDPEndPoint endPoint, string nameIdFormat) { Trace.TraceMethodCalled(GetType(), "PerformQuery()"); HttpSOAPBindingBuilder builder = new HttpSOAPBindingBuilder(context); NameID name = new NameID(); name.Value = Saml20Identity.Current.Name; name.Format = nameIdFormat; _attrQuery.Subject.Items = new object[] { name }; _attrQuery.SamlAttribute = _attributes.ToArray(); XmlDocument query = new XmlDocument(); query.LoadXml(Serialization.SerializeToXmlString(_attrQuery)); XmlSignatureUtils.SignDocument(query, ID); if (query.FirstChild is XmlDeclaration) { query.RemoveChild(query.FirstChild); } Stream s; if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAttrQuery, endPoint.metadata.GetAttributeQueryEndpointLocation(), query.OuterXml)); } try { s = builder.GetResponse(endPoint.metadata.GetAttributeQueryEndpointLocation(), query.OuterXml, endPoint.AttributeQuery); }catch (Exception e) { Trace.TraceData(TraceEventType.Error, e.ToString()); throw; } HttpSOAPBindingParser parser = new HttpSOAPBindingParser(s); Status status = parser.GetStatus(); if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { Trace.TraceData(TraceEventType.Error, string.Format(Tracing.AttrQueryStatusError, Serialization.SerializeToXmlString(status))); throw new Saml20Exception(status.StatusMessage); } bool isEncrypted; XmlElement xmlAssertion = Saml20SignonHandler.GetAssertion(parser.SamlMessage, out isEncrypted); if (isEncrypted) { Saml20EncryptedAssertion ass = new Saml20EncryptedAssertion( (RSA)FederationConfig.GetConfig().SigningCertificate.GetCertificate().PrivateKey); ass.LoadXml(xmlAssertion); ass.Decrypt(); xmlAssertion = ass.Assertion.DocumentElement; } Saml20Assertion assertion = new Saml20Assertion(xmlAssertion, null, AssertionProfile.Core, endPoint.QuirksMode); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.AttrQueryAssertion, xmlAssertion == null ? string.Empty : xmlAssertion.OuterXml)); } if (!assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endPoint.metadata.Keys, endPoint))) { Trace.TraceData(TraceEventType.Error, Resources.SignatureInvalid); throw new Saml20Exception(Resources.SignatureInvalid); } foreach (SamlAttribute attr in assertion.Attributes) { Saml20Identity.Current.AddAttributeFromQuery(attr.Name, attr); } }
private void HandleSOAP(HttpContext context, Stream inputStream) { Trace.TraceMethodCalled(GetType(), "HandleSOAP"); HttpArtifactBindingParser parser = new HttpArtifactBindingParser(inputStream); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); if (parser.IsArtifactResolve()) { Trace.TraceData(TraceEventType.Information, Tracing.ArtifactResolveIn); IDPEndPoint idp = RetrieveIDPConfiguration(parser.Issuer); AuditLogging.IdpId = idp.Id; AuditLogging.AssertionId = parser.ArtifactResolve.ID; if (!parser.CheckSamlMessageSignature(idp.metadata.Keys)) { HandleError(context, "Invalid Saml message signature"); AuditLogging.logEntry(Direction.IN, Operation.ARTIFACTRESOLVE, "Could not verify signature", parser.SamlMessage); } ; builder.RespondToArtifactResolve(parser.ArtifactResolve); } else if (parser.IsArtifactResponse()) { Trace.TraceData(TraceEventType.Information, Tracing.ArtifactResponseIn); Status status = parser.ArtifactResponse.Status; if (status.StatusCode.Value != Saml20Constants.StatusCodes.Success) { HandleError(context, status); AuditLogging.logEntry(Direction.IN, Operation.ARTIFACTRESOLVE, string.Format("Illegal status for ArtifactResponse {0} expected 'Success', msg: {1}", status.StatusCode.Value, parser.SamlMessage)); return; } if (parser.ArtifactResponse.Any.LocalName == Response.ELEMENT_NAME) { bool isEncrypted; XmlElement assertion = GetAssertion(parser.ArtifactResponse.Any, out isEncrypted); if (assertion == null) { HandleError(context, "Missing assertion"); } if (isEncrypted) { HandleEncryptedAssertion(context, assertion); } else { HandleAssertion(context, assertion); } } else { AuditLogging.logEntry(Direction.IN, Operation.ARTIFACTRESOLVE, string.Format("Unsupported payload message in ArtifactResponse: {0}, msg: {1}", parser.ArtifactResponse.Any.LocalName, parser.SamlMessage)); HandleError(context, string.Format("Unsupported payload message in ArtifactResponse: {0}", parser.ArtifactResponse.Any.LocalName)); } } else { Status s = parser.GetStatus(); if (s != null) { HandleError(context, s); } else { AuditLogging.logEntry(Direction.IN, Operation.ARTIFACTRESOLVE, string.Format("Unsupported SamlMessage element: {0}, msg: {1}", parser.SamlMessageName, parser.SamlMessage)); HandleError(context, string.Format("Unsupported SamlMessage element: {0}", parser.SamlMessageName)); } } }