/// <summary> /// Handles all artifact creations and redirects. /// </summary> /// <param name="destination">The destination.</param> /// <param name="localEndpointIndex">Index of the local endpoint.</param> /// <param name="signedSamlMessage">The signed saml message.</param> /// <param name="relayState">The query string relay state value to add to the communication</param> private void ArtifactRedirect(IDPEndPointElement destination, Int16 localEndpointIndex, XmlDocument signedSamlMessage, string relayState) { SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); string sourceId = config.ServiceProvider.ID; byte[] sourceIdHash = ArtifactUtil.GenerateSourceIdHash(sourceId); byte[] messageHandle = ArtifactUtil.GenerateMessageHandle(); string artifact = ArtifactUtil.CreateArtifact(HttpArtifactBindingConstants.ArtifactTypeCode, localEndpointIndex, sourceIdHash, messageHandle); _context.Cache.Insert(artifact, signedSamlMessage, null, DateTime.Now.AddMinutes(1), Cache.NoSlidingExpiration); string destinationUrl = destination.Url + "?" + HttpArtifactBindingConstants.ArtifactQueryStringName + "=" + HttpUtility.UrlEncode(artifact); if (!string.IsNullOrEmpty(relayState)) { destinationUrl += "&relayState=" + relayState; } if(Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.CreatedArtifact, artifact, signedSamlMessage.OuterXml)); } _context.Response.Redirect(destinationUrl); }
/// <summary> /// Parses the metadata files found in the directory specified in the configuration. /// </summary> private Saml20MetadataDocument ParseFile(string file) { XmlDocument doc = LoadFileAsXmlDocument(file); //_fileInfo[file] = File.GetLastWriteTime(file); // Mark that we have seen the file. try { foreach (XmlNode child in doc.ChildNodes) { if (child.NamespaceURI == Saml20Constants.METADATA) { if (child.LocalName == EntityDescriptor.ELEMENT_NAME) { return(new Saml20MetadataDocument(doc)); } // TODO Decide how to handle several entities in one metadata file. if (child.LocalName == EntitiesDescriptor.ELEMENT_NAME) { throw new NotImplementedException(); } } } // No entity descriptor found. throw new InvalidDataException(); // BAIIIIIIL!! } catch (Exception e) { // Probably not a metadata file. Trace.TraceData(TraceEventType.Error, file, "Probably not a SAML2.0 metadata file.", e.ToString()); return(null); } }
/// <summary> /// Initializes a new instance of the <see cref="Saml20LogoutHandler"/> class. /// </summary> public Saml20LogoutHandler() { // Read the proper redirect url from config try { RedirectUrl = SAML20FederationConfig.GetConfig().ServiceProvider.LogoutEndpoint.RedirectUrl; ErrorBehaviour = SAML20FederationConfig.GetConfig().ServiceProvider.LogoutEndpoint.ErrorBehaviour.ToString(); } catch (Exception e) { if (Trace.ShouldTrace(TraceEventType.Error)) { Trace.TraceData(TraceEventType.Error, e.ToString()); } } }
/// <summary> /// Initializes a new instance of the <see cref="Saml20SignonHandler"/> class. /// </summary> public Saml20SignonHandler() { _certificate = FederationConfig.GetConfig().SigningCertificate.GetCertificate(); // Read the proper redirect url from config try { RedirectUrl = SAML20FederationConfig.GetConfig().ServiceProvider.SignOnEndpoint.RedirectUrl; ErrorBehaviour = SAML20FederationConfig.GetConfig().ServiceProvider.SignOnEndpoint.ErrorBehaviour.ToString(); } catch (Exception e) { if (Trace.ShouldTrace(TraceEventType.Error)) { Trace.TraceData(TraceEventType.Error, e.ToString()); } } }
private static XmlDocument GetDecodedSamlResponse(HttpContext context, Encoding encoding) { string base64 = context.Request.Params["SAMLResponse"]; XmlDocument doc = new XmlDocument(); doc.XmlResolver = null; doc.PreserveWhitespace = true; string samlResponse = encoding.GetString(Convert.FromBase64String(base64)); if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, "Decoded SAMLResponse", samlResponse); } doc.LoadXml(samlResponse); return(doc); }
public void Load(string base64AuthorisationsAttribute) { try { string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(base64AuthorisationsAttribute)); AuthorisationsType authorisations = Serialization.DeserializeFromXmlString <AuthorisationsType>(decoded); _autorisations = authorisations; _isLoaded = true; } catch (Exception e) { _isLoaded = false; if (Trace.ShouldTrace(TraceEventType.Error)) { Trace.TraceData(TraceEventType.Error, string.Format(Errors.DecodeFailed, base64AuthorisationsAttribute, e.Message)); } throw new Saml20BRSException(Errors.DecodeFailedMsg, e); } }
/// <summary> /// Performs the attribute query and adds the resulting attributes to Saml20Identity.Current. /// </summary> /// <param name="context">The http context.</param> public void PerformQuery(HttpContext context) { SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); string endpointId = Saml20PrincipalCache.GetSaml20AssertionLite().Issuer; if (string.IsNullOrEmpty(endpointId)) { Trace.TraceData(TraceEventType.Information, Tracing.AttrQueryNoLogin); throw new InvalidOperationException(Tracing.AttrQueryNoLogin); } IDPEndPoint ep = config.FindEndPoint(endpointId); if (ep == null) { throw new Saml20Exception(string.Format("Unable to find information about the IdP with id \"{0}\"", endpointId)); } PerformQuery(context, ep); }
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); SessionFactory.SessionContext.AbandonAllSessions(userId); Trace.TraceData(TraceEventType.Verbose, "Sessions cleared."); } }
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 { // 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> /// Handles responses to an artifact resolve message. /// </summary> /// <param name="artifactResolve">The artifact resolve message.</param> public void RespondToArtifactResolve(ArtifactResolve artifactResolve) { XmlDocument samlDoc = (XmlDocument)_context.Cache.Get(artifactResolve.Artifact); Saml20ArtifactResponse response = Saml20ArtifactResponse.GetDefault(); response.StatusCode = Saml20Constants.StatusCodes.Success; response.InResponseTo = artifactResolve.ID; response.SamlElement = samlDoc.DocumentElement; XmlDocument responseDoc = response.GetXml(); if (responseDoc.FirstChild is XmlDeclaration) responseDoc.RemoveChild(responseDoc.FirstChild); XmlSignatureUtils.SignDocument(responseDoc, response.ID); if(Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.RespondToArtifactResolve, artifactResolve.Artifact, responseDoc.OuterXml)); } SendResponseMessage(responseDoc.OuterXml); }
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()"); } }
/// <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> /// 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)); 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 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; 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(); 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 = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value; request.SessionIndex = Saml20PrincipalCache.GetSaml20AssertionLite().SessionIndex; 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 = Saml20PrincipalCache.GetSaml20AssertionLite().Subject.Value; request.SessionIndex = Saml20PrincipalCache.GetSaml20AssertionLite().SessionIndex; 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; Saml20AssertionLite saml20AssertionLite = Saml20PrincipalCache.GetSaml20AssertionLite(); IDPEndPoint endpoint = RetrieveIDPConfiguration(saml20AssertionLite.Issuer); 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)); } 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(); 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 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)); } } }
/// <summary> /// Validates the Assertion's conditions /// Audience restrictions processing rules are: /// - Within a single audience restriction condition in the assertion, the service must be configured /// with an audience-list that contains at least one of the restrictions in the assertion ("OR" filter) /// - When multiple audience restrictions are present within the same assertion, all individual audience /// restriction conditions must be met ("AND" filter) /// </summary> private void ValidateConditions(Assertion assertion) { // Conditions are not required if (assertion.Conditions == null) { return; } bool oneTimeUseSeen = false; bool proxyRestrictionsSeen = false; ValidateConditionsInterval(assertion.Conditions); foreach (ConditionAbstract cat in assertion.Conditions.Items) { if (cat is OneTimeUse) { if (oneTimeUseSeen) { throw new Saml20FormatException("Assertion contained more than one condition of type OneTimeUse"); } oneTimeUseSeen = true; continue; } if (cat is ProxyRestriction) { if (proxyRestrictionsSeen) { throw new Saml20FormatException("Assertion contained more than one condition of type ProxyRestriction"); } proxyRestrictionsSeen = true; ProxyRestriction proxyRestriction = (ProxyRestriction)cat; if (!String.IsNullOrEmpty(proxyRestriction.Count)) { uint res; if (!UInt32.TryParse(proxyRestriction.Count, out res)) { throw new Saml20FormatException("Count attribute of ProxyRestriction MUST BE a non-negative integer"); } } if (proxyRestriction.Audience != null) { foreach (string audience in proxyRestriction.Audience) { if (!Uri.IsWellFormedUriString(audience, UriKind.Absolute)) { throw new Saml20FormatException("ProxyRestriction Audience MUST BE a wellformed uri"); } } } } // AudienceRestriction processing goes here (section 2.5.1.4 of [SAML2.0std]) if (cat is AudienceRestriction) { // No audience restrictions? No problems... AudienceRestriction audienceRestriction = (AudienceRestriction)cat; if (audienceRestriction.Audience == null || audienceRestriction.Audience.Count == 0) { continue; } // If there are no allowed audience uris configured for the service, the assertion is not // valid for this service if (_allowedAudienceUris == null || _allowedAudienceUris.Count < 1) { throw new Saml20FormatException("The service is not configured to meet any audience restrictions"); } string match = null; foreach (string audience in audienceRestriction.Audience) { //In QuirksMode this validation is omitted if (!_quirksMode) { // The given audience value MUST BE a valid URI if (!Uri.IsWellFormedUriString(audience, UriKind.Absolute)) { throw new Saml20FormatException("Audience element has value which is not a wellformed absolute uri"); } } match = _allowedAudienceUris.Find( delegate(string allowedUri) { return(allowedUri.Equals(audience)); }); if (match != null) { break; } } if (Trace.ShouldTrace(TraceEventType.Verbose)) { string intended = "Intended uris: " + Environment.NewLine + String.Join(Environment.NewLine, audienceRestriction.Audience.ToArray()); string allowed = "Allowed uris: " + Environment.NewLine + String.Join(Environment.NewLine, _allowedAudienceUris.ToArray()); Trace.TraceData(TraceEventType.Verbose, Trace.CreateTraceString(GetType(), "ValidateConditions"), intended, allowed); } if (match == null) { throw new Saml20FormatException("The service is not configured to meet the given audience restrictions"); } } } }
private void TransferClient(IDPEndPoint idpEndpoint, Saml20AuthnRequest request, HttpContext context) { AuditLogging.AssertionId = request.ID; AuditLogging.IdpId = idpEndpoint.Id; // Determine which endpoint to use from the configuration file or the endpoint metadata. IDPEndPointElement destination = DetermineEndpointConfiguration(SAMLBinding.REDIRECT, idpEndpoint.SSOEndpoint, idpEndpoint.metadata.SSOEndpoints()); request.Destination = destination.Url; bool isPassive; string isPassiveAsString = context.Request.Params[IDPIsPassive]; if (bool.TryParse(isPassiveAsString, out isPassive)) { request.IsPassive = isPassive; } if (idpEndpoint.IsPassive) { request.IsPassive = true; } bool forceAuthn; string forceAuthnAsString = context.Request.Params[IDPForceAuthn]; if (bool.TryParse(forceAuthnAsString, out forceAuthn)) { request.ForceAuthn = forceAuthn; } if (idpEndpoint.ForceAuthn) { request.ForceAuthn = true; } if (idpEndpoint.SSOEndpoint != null) { if (!string.IsNullOrEmpty(idpEndpoint.SSOEndpoint.ForceProtocolBinding)) { request.ProtocolBinding = idpEndpoint.SSOEndpoint.ForceProtocolBinding; } } //Save request message id to session SessionFactory.SessionContext.Current[SessionConstants.ExpectedInResponseTo] = request.ID; //Save request message id to session context.Session.Add(ExpectedInResponseToSessionKey, request.ID); if (destination.Binding == SAMLBinding.REDIRECT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Redirect, idpEndpoint.Id)); HttpRedirectBindingBuilder builder = new HttpRedirectBindingBuilder(); builder.signingKey = _certificate.PrivateKey; builder.Request = request.GetXml().OuterXml; string s = request.Destination + "?" + builder.ToQuery(); AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT, "Redirecting user to IdP for authentication", builder.Request); context.Response.Redirect(s, true); return; } if (destination.Binding == SAMLBinding.POST) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Post, idpEndpoint.Id)); HttpPostBindingBuilder builder = new HttpPostBindingBuilder(destination); //Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HTTP_Post; } XmlDocument req = request.GetXml(); XmlSignatureUtils.SignDocument(req, request.ID); builder.Request = req.OuterXml; AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_POST); builder.GetPage().ProcessRequest(context); return; } if (destination.Binding == SAMLBinding.ARTIFACT) { Trace.TraceData(TraceEventType.Information, string.Format(Tracing.SendAuthnRequest, Saml20Constants.ProtocolBindings.HTTP_Artifact, idpEndpoint.Id)); HttpArtifactBindingBuilder builder = new HttpArtifactBindingBuilder(context); //Honor the ForceProtocolBinding and only set this if it's not already set if (string.IsNullOrEmpty(request.ProtocolBinding)) { request.ProtocolBinding = Saml20Constants.ProtocolBindings.HTTP_Artifact; } AuditLogging.logEntry(Direction.OUT, Operation.AUTHNREQUEST_REDIRECT_ARTIFACT); builder.RedirectFromLogin(destination, request); } HandleError(context, Resources.BindingError); }
/// <summary> /// Handles the selection of an IDP. If only one IDP is found, the user is automatically redirected to it. /// If several are found, and nothing indicates to which one the user should be sent, this method returns null. /// </summary> public IDPEndPoint RetrieveIDP(HttpContext context) { SAML20FederationConfig config = SAML20FederationConfig.GetConfig(); //If idpChoice is set, use it value if (!string.IsNullOrEmpty(context.Request.Params[IDPChoiceParameterName])) { AuditLogging.logEntry(Direction.IN, Operation.DISCOVER, "Using IDPChoiceParamater: " + context.Request.Params[IDPChoiceParameterName]); IDPEndPoint endPoint = config.FindEndPoint(context.Request.Params[IDPChoiceParameterName]); if (endPoint != null) { return(endPoint); } } //If we have a common domain cookie, use it's value //It must have been returned from the local common domain cookie reader endpoint. if (!string.IsNullOrEmpty(context.Request.QueryString["_saml_idp"])) { CommonDomainCookie cdc = new CommonDomainCookie(context.Request.QueryString["_saml_idp"]); if (cdc.IsSet) { IDPEndPoint endPoint = config.FindEndPoint(cdc.PreferredIDP); if (endPoint != null) { if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, "IDP read from Common Domain Cookie: " + cdc.PreferredIDP); } return(endPoint); } AuditLogging.logEntry(Direction.IN, Operation.DISCOVER, "Invalid IdP in Common Domain Cookie, IdP not found in list of IdPs: " + cdc.PreferredIDP); } } //If there is only one configured IDPEndPoint lets just use that if (config.IDPEndPoints.Count == 1 && config.IDPEndPoints[0].metadata != null) { AuditLogging.logEntry(Direction.IN, Operation.DISCOVER, "No IdP selected in Common Domain Cookie, using default IdP: " + config.IDPEndPoints[0].Name); return(config.IDPEndPoints[0]); } // If one of the endpoints are marked with default, use that one var defaultIdp = config.Endpoints.IDPEndPoints.Find(idp => idp.Default); if (defaultIdp != null) { if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, "Using IdP marked as default: " + defaultIdp.Id); } return(defaultIdp); } // In case an Idp selection url has been configured, redirect to that one. if (!string.IsNullOrEmpty(config.Endpoints.idpSelectionUrl)) { if (Trace.ShouldTrace(TraceEventType.Information)) { Trace.TraceData(TraceEventType.Information, "Redirecting to idpSelectionUrl for selection of IDP: " + config.Endpoints.idpSelectionUrl); } context.Response.Redirect(config.Endpoints.idpSelectionUrl); } // If an IDPSelectionEvent handler is present, request the handler for an IDP endpoint to use. var idpEndpoint = IDPSelectionUtil.InvokeIDPSelectionEventHandler(config.Endpoints); if (idpEndpoint != null) { return(idpEndpoint); } return(null); }