private void DoLogin(HttpContext context, Saml20Assertion assertion) { SessionFactory.SessionContext.AssociateUserIdWithCurrentSession(assertion.Subject.Value); SessionFactory.SessionContext.Current[SessionConstants.Saml20AssertionLite] = Saml20AssertionLite.ToLite(assertion); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.Login, assertion.Subject.Value, assertion.SessionIndex, assertion.Subject.Format)); } 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> /// 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); }
private void DoLogout(HttpContext context, bool IdPInitiated) { try { foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LogoutAction()"); action.LogoutAction(this, context, IdPInitiated); Trace.TraceMethodDone(action.GetType(), "LogoutAction()"); } } finally { if (SessionStore.CurrentSession != null) { // Always end with abandoning the session. Trace.TraceData(TraceEventType.Information, "Clearing session for userId: " + Saml20Identity.Current.Name); SessionStore.AbandonAllSessions(Saml20Identity.Current.Name); Trace.TraceData(TraceEventType.Verbose, "Session cleared."); } else { Trace.TraceData(TraceEventType.Warning, "The user was logged out but the session had already expired. Distributed session could therefore not be abandonded½"); } } }
/// <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 PreventOpenRedirectAttack has been enabled ... the return URL is only set if the URL is local. if (!string.IsNullOrEmpty(returnUrl) && (!FederationConfig.GetConfig().PreventOpenRedirectAttack || IsLocalUrl(returnUrl))) { SessionStore.CurrentSession[SessionConstants.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; } //VALTECH: 2011-05-19: Changed constructor to one using default assertion profile //ORIGINAL: 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); 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> /// 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(); // VALTECH: Publish AssertionConsumerServiceURL which is missing in DK framework. Reguired by Logica idp. authnRequest.Request.AssertionConsumerServiceURL = SAML20FederationConfig.GetConfig().ServiceProvider.AssertionConsumerServiceURL ?? context.Request.Url.ToString(); TransferClient(idpEndpoint, authnRequest, context); }
/// <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)) { SessionFactory.SessionContext.Current[SessionConstants.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); }
private void DoLogin(HttpContext context, Saml20Assertion assertion) { SessionStore.AssociateUserIdWithCurrentSession(assertion.Subject.Value); // The assertion is what keeps the session alive. If it is ever removed ... the session will appear as removed in the SessionStoreProvider because Saml20AssertionLite is the only thing kept in session store when login flow is completed.. SessionStore.CurrentSession[SessionConstants.Saml20AssertionLite] = Saml20AssertionLite.ToLite(assertion); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.Login, assertion.Subject.Value, assertion.SessionIndex, assertion.Subject.Format)); } string assuranceLevel = GetAssuranceLevel(assertion) ?? "(Unknown)"; 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, FederationConfig.GetConfig().SessionTimeout)); foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LoginAction()"); action.LoginAction(this, context, assertion); Trace.TraceMethodDone(action.GetType(), "LoginAction()"); } }
/// <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; Saml20AssertionLite saml20AssertionLite = Saml20PrincipalCache.GetSaml20AssertionLite(); if (saml20AssertionLite != null) { idpEndpoint = RetrieveIDPConfiguration(saml20AssertionLite.Issuer); } 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"); }
/// <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); })) { SessionStore.AssertSessionExists(); HandleSOAP(context, context.Request.InputStream); return; } if (!string.IsNullOrEmpty(context.Request.Params["SAMLart"])) { SessionStore.AssertSessionExists(); HandleArtifact(context); } if (!string.IsNullOrEmpty(context.Request.Params["SamlResponse"])) { SessionStore.AssertSessionExists(); 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."); SessionStore.CreateSessionIfNotExists(); SendRequest(context); } } }
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); } var signingCertificate = FederationConfig.GetConfig().GetFirstValidCertificate(); var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(idpEndPoint.ShaHashingAlgorithm); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(doc, resolve.ID, signingCertificate); 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)); }
private void DoSoapLogout(HttpContext context, string userId) { try { foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "SoapLogoutAction()"); action.SoapLogoutAction(this, context, userId); Trace.TraceMethodDone(action.GetType(), "SoapLogoutAction()"); } } finally { // Always end with abandoning the session. Trace.TraceData(TraceEventType.Information, "Clearing all sessions related to user with id: " + userId); SessionStore.AbandonAllSessions(userId); Trace.TraceData(TraceEventType.Verbose, "Sessions cleared."); } }
private void DoLogout(SamlHttpContext context, bool IdPInitiated) { try { foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LogoutAction()"); action.LogoutAction(this, context, IdPInitiated); Trace.TraceMethodDone(action.GetType(), "LogoutAction()"); } } finally { // Always end with abandoning the session. Trace.TraceData(TraceEventType.Information, "Clearing session with id: " + SessionFactory.SessionContext.Current.Id); SessionFactory.SessionContext.AbandonAllSessions(Saml20Identity.Current.Name); //SessionFactory.SessionContext.AbandonCurrentSession(); Trace.TraceData(TraceEventType.Verbose, "Session cleared."); } }
/// <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)); }
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; if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.Login, assertion.Subject.Value, assertion.SessionIndex, assertion.Subject.Format)); } AuditLogging.logEntry(Direction.IN, Operation.LOGIN, "Subject: " + assertion.Subject.Value + " NameIDFormat: " + assertion.Subject.Format); foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LoginAction()"); action.LoginAction(this, context, assertion); Trace.TraceMethodDone(action.GetType(), "LoginAction()"); } }
private void DoLogin(HttpContext context, Saml20Assertion assertion) { SessionFactory.SessionContext.AssociateUserIdWithCurrentSession(assertion.Subject.Value); SessionFactory.SessionContext.Current[SessionConstants.Saml20AssertionLite] = Saml20AssertionLite.ToLite(assertion); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.Login, assertion.Subject.Value, assertion.SessionIndex, assertion.Subject.Format)); } string assuranceLevel = GetAssuranceLevel(assertion) ?? "(Unknown)"; 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, FederationConfig.GetConfig().SessionTimeout)); foreach (IAction action in Actions.Actions.GetActions()) { Trace.TraceMethodCalled(action.GetType(), "LoginAction()"); action.LoginAction(this, context, assertion); Trace.TraceMethodDone(action.GetType(), "LoginAction()"); } }
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(idp, 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)); } } }
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)); } } }
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, string.Format(Resources.UnsupportedRequestType, context.Request.RequestType)); return; } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); // Check that idp in session and request matches. string idpRequest = logoutRequest.Issuer.Value; // SessionFactory.SessionContext.Current.New is never the first call to Current due to the logic in Application_AuthenticateRequest() ... Saml20Identity.IsInitialized() // Hence we need to check on Saml20Identity.IsInitialized() instead of using SessionFactory.SessionContext.Current.New. bool isOioSamlSessionActive = Saml20Identity.IsInitialized(); if (isOioSamlSessionActive) { object idpId = Saml20PrincipalCache.GetSaml20AssertionLite().Issuer; if (idpId != null && idpId.ToString() != idpRequest) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, string.Format(Resources.IdPMismatchBetweenRequestAndSession, idpId, 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 && isOioSamlSessionActive) { // 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 var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(endpoint.ShaHashingAlgorithm); 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; builder.ShaHashingAlgorithm = shaHashingAlgorithm; 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(); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(responseDocument, response.ID, signingCertificate); 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, string.Format(Resources.UnsupportedRequestType, context.Request.RequestType)); } XmlDocument doc = new XmlDocument(); doc.XmlResolver = null; 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 HandleRequest(HttpContext context) { Trace.TraceMethodCalled(GetType(), "HandleRequest()"); //Fetch the endpoint configuration IDPEndPoint idpEndpoint = RetrieveIDPConfiguration(context.Session[IDPLoginSessionKey].ToString()); IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SLOEndpoint, idpEndpoint.metadata.SLOEndpoints()); //Fetch config object SAML20FederationConfig config = ConfigurationReader.GetConfig <SAML20FederationConfig>(); //Build the response object Saml20LogoutResponse response = new Saml20LogoutResponse(); response.Issuer = config.ServiceProvider.ID; response.Destination = destination.Url; response.StatusCode = Saml20Constants.StatusCodes.Success; string message = string.Empty; if (context.Request.RequestType == "GET") // HTTP Redirect binding { HttpRedirectBindingParser parser = new HttpRedirectBindingParser(context.Request.Url); IDPEndPoint endpoint = config.FindEndPoint(idpEndpoint.Id); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); HandleError(context, "Cannot find metadata for IdP " + idpEndpoint.Id); return; } Saml20MetadataDocument metadata = endpoint.metadata; if (!parser.VerifySignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Invalid signature redirect-binding, msg: " + parser.Message); HandleError(context, Resources.SignatureInvalid); return; } message = parser.Message; } else if (context.Request.RequestType == "POST") // HTTP Post binding { HttpPostBindingParser parser = new HttpPostBindingParser(context); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present, msg: " + parser.Message); HandleError(context, Resources.SignatureNotPresent); } IDPEndPoint endpoint = config.FindEndPoint(idpEndpoint.Id); if (endpoint.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP"); HandleError(context, "Cannot find metadata for IdP " + idpEndpoint.Id); return; } Saml20MetadataDocument metadata = endpoint.metadata; // handle a logout-request if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Invalid signature post-binding, msg: " + parser.Message); HandleError(context, Resources.SignatureInvalid); } message = parser.Message; } else { //Error: We don't support HEAD, PUT, CONNECT, TRACE, DELETE and OPTIONS HandleError(context, Resources.UnsupportedRequestTypeFormat(context.Request.RequestType)); } AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, message); //Log the user out locally DoLogout(context, true); LogoutRequest req = Serialization.DeserializeFromXmlString <LogoutRequest>(message); response.InResponseTo = req.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; } }
/// <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); 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) { IEnumerable <string> validationFailures; if (!assertion.CheckSignature(GetTrustedSigners(endp.metadata.GetKeys(KeyTypes.signing), endp, out validationFailures))) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Invalid signature, assertion: " + elem); string errorMessage = Resources.SignatureInvalid; validationFailures = validationFailures.ToArray(); if (validationFailures.Any()) { errorMessage += $"\nVerification of IDP certificate used for signature failed from the following certificate checks:\n{string.Join("\n", validationFailures)}"; } HandleError(context, errorMessage); return; } } if (assertion.IsExpired()) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion expired, assertion: " + elem.OuterXml); HandleError(context, Resources.AssertionExpired); return; } // Only check if assertion has the required assurancelevel if it is present. string assuranceLevel = GetAssuranceLevel(assertion); string minimumAssuranceLevel = SAML20FederationConfig.GetConfig().MinimumAssuranceLevel; if (assuranceLevel != null) { // Assurance level is ok if the string matches the configured minimum assurance level. This is in order to support the value "Test". However, normally the value will be an integer if (assuranceLevel != minimumAssuranceLevel) { // If strings are different it is still ok if the assertion has stronger assurance level than the minimum required. int assuranceLevelAsInt; int minimumAssuranceLevelAsInt; if (!int.TryParse(assuranceLevel, out assuranceLevelAsInt) || !int.TryParse(minimumAssuranceLevel, out minimumAssuranceLevelAsInt) || assuranceLevelAsInt < minimumAssuranceLevelAsInt) { string errorMessage = string.Format(Resources.AssuranceLevelTooLow, assuranceLevel, minimumAssuranceLevel); AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, errorMessage + " Assertion: " + elem.OuterXml); HandleError(context, string.Format(Resources.AssuranceLevelTooLow, assuranceLevel, minimumAssuranceLevel)); return; } } } CheckConditions(context, assertion); AuditLogging.AssertionId = assertion.Id; AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion validated succesfully"); DoLogin(context, assertion); }
/// <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); assertion.Validate(DateTime.UtcNow); 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) { IEnumerable <string> validationFailures; if (!assertion.CheckSignature(GetTrustedSigners(endp.metadata.GetKeys(KeyTypes.signing), endp, out validationFailures))) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Invalid signature, assertion: " + elem); string errorMessage = Resources.SignatureInvalid; validationFailures = validationFailures.ToArray(); if (validationFailures.Any()) { errorMessage += $"\nVerification of IDP certificate used for signature failed from the following certificate checks:\n{string.Join("\n", validationFailures)}"; } HandleError(context, errorMessage); return; } } if (assertion.IsExpired()) { AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion expired, assertion: " + elem.OuterXml); HandleError(context, Resources.AssertionExpired); return; } if (!ValidateLoA(context, assertion, elem)) { return; } CheckConditions(context, assertion); AuditLogging.AssertionId = assertion.Id; AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion validated succesfully"); DoLogin(context, assertion); }
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); var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(idp.ShaHashingAlgorithm); 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(idp, 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; IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idp.SLOEndpoint, idp.metadata.SLOEndpoints()); builder.RedirectFromLogout(idp, 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)); } Saml20LogoutResponse response = new Saml20LogoutResponse(); if (!parser.IsSigned()) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Signature not present in SOAP logout request, msg: " + parser.SamlMessage.OuterXml); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } if (idp.metadata == null) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Cannot find metadata for IdP: " + parser.Issuer); // Not able to process the request as we do not know the IdP. response.StatusCode = Saml20Constants.StatusCodes.NoAvailableIDP; } else { Saml20MetadataDocument metadata = idp.metadata; if (!parser.CheckSignature(metadata.GetKeys(KeyTypes.signing))) { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. Invalid signature SOAP logout, msg: " + parser.SamlMessage.OuterXml); response.StatusCode = Saml20Constants.StatusCodes.RequestDenied; } } if (parser.GetNameID() != null && !string.IsNullOrEmpty(parser.GetNameID().Value)) { DoSoapLogout(context, parser.GetNameID().Value); } else { AuditLogging.logEntry(Direction.IN, Operation.LOGOUTREQUEST, "Request has been denied. No user ID was supplied in SOAP logout request, msg: " + parser.SamlMessage.OuterXml); response.StatusCode = Saml20Constants.StatusCodes.NoAuthnContext; } LogoutRequest req = parser.LogoutRequest; //Build the response object response.Issuer = config.ServiceProvider.ID; response.StatusCode = Saml20Constants.StatusCodes.Success; response.InResponseTo = req.ID; XmlDocument doc = response.GetXml(); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(doc, response.ID, signingCertificate); 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)); } } }
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; request.SubjectToLogOut.Format = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Format; var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(endpoint.ShaHashingAlgorithm); if (destination.Binding == SAMLBinding.POST) { HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); request.Destination = destination.Url; request.Reason = Saml20Constants.Reasons.User; request.SubjectToLogOut.Value = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value; request.SessionIndex = Saml20PrincipalCache.GetSaml20AssertionLite().SessionIndex; XmlDocument requestDocument = request.GetXml(); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(requestDocument, request.ID, signingCertificate); 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 = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value; request.SessionIndex = Saml20PrincipalCache.GetSaml20AssertionLite().SessionIndex; builder.Request = request.GetXml().OuterXml; builder.ShaHashingAlgorithm = shaHashingAlgorithm; 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 = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value; request.SessionIndex = Saml20PrincipalCache.GetSaml20AssertionLite().SessionIndex; HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); AuditLogging.logEntry(Direction.OUT, Operation.LOGOUTREQUEST, "Method: Artifact"); builder.RedirectFromLogout(endpoint, destination, request, Guid.NewGuid().ToString("N")); } HandleError(context, Resources.BindingError); }
/// <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.XmlResolver = null; query.LoadXml(Serialization.SerializeToXmlString(_attrQuery)); var signingCertificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); var shaHashingAlgorithm = SignatureProviderFactory.ValidateShaHashingAlgorithm(endPoint.ShaHashingAlgorithm); var signatureProvider = SignatureProviderFactory.CreateFromShaHashingAlgorithmName(shaHashingAlgorithm); signatureProvider.SignAssertion(query, ID, signingCertificate); 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); assertion.Validate(DateTime.UtcNow); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.AttrQueryAssertion, xmlAssertion == null ? string.Empty : xmlAssertion.OuterXml)); } IEnumerable <string> validationFailures; if (!assertion.CheckSignature(Saml20SignonHandler.GetTrustedSigners(endPoint.metadata.Keys, endPoint, out validationFailures))) { Trace.TraceData(TraceEventType.Error, Resources.SignatureInvalid); throw new Saml20Exception(Resources.SignatureInvalid); } foreach (SamlAttribute attr in assertion.Attributes) { Saml20Identity.Current.AddAttributeFromQuery(attr.Name, attr); } }
/// <summary> /// Deserializes an assertion, verifies its signature and logs in the user if the assertion is valid. /// </summary> private void HandleAssertion(SamlHttpContext 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, 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) { // var keys = endp.metadata.GetKeys(KeyTypes.signing); // don't be so fussy about the key type (e.g. Centrify's keys are only marked as encryption keys) var keys = endp.metadata.Keys; if (!assertion.CheckSignature(GetTrustedSigners(keys, 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; } // Only check if assertion has the required assurancelevel if it is present. string assuranceLevel = GetAssuranceLevel(assertion); string minimumAssuranceLevel = SAML20FederationConfig.GetConfig().MinimumAssuranceLevel; if (assuranceLevel != null) { // Assurance level is ok if the string matches the configured minimum assurance level. This is in order to support the value "Test". However, normally the value will be an integer if (assuranceLevel != minimumAssuranceLevel) { // If strings are different it is still ok if the assertion has stronger assurance level than the minimum required. int assuranceLevelAsInt; int minimumAssuranceLevelAsInt; if (!int.TryParse(assuranceLevel, out assuranceLevelAsInt) || !int.TryParse(minimumAssuranceLevel, out minimumAssuranceLevelAsInt) || assuranceLevelAsInt < minimumAssuranceLevelAsInt) { string errorMessage = string.Format(Resources.AssuranceLevelTooLow, assuranceLevel, minimumAssuranceLevel); AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, errorMessage + " Assertion: " + elem.OuterXml); HandleError(context, string.Format(Resources.AssuranceLevelTooLow, assuranceLevel, minimumAssuranceLevel)); return; } } } CheckConditions(context, assertion); AuditLogging.AssertionId = assertion.Id; AuditLogging.logEntry(Direction.IN, Operation.AUTHNREQUEST_POST, "Assertion validated succesfully"); DoLogin(context, assertion); }