File: System\IdentityModel\Diagnostics\SecurityTraceRecordHelper.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)

namespace System.IdentityModel.Diagnostics
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IdentityModel.Diagnostics;
    using System.Diagnostics;
    using System.Runtime.Diagnostics;
    using System.Security.Authentication.ExtendedProtection;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.ServiceModel.Diagnostics;
    using System.Xml;
    using System.IdentityModel.Tokens;
    using System.Security.Cryptography;
 
    class SecurityTraceRecord : TraceRecord
    {
        String traceName;
        internal SecurityTraceRecord(String traceName)
        {
            if (string.IsNullOrEmpty(traceName))
                this.traceName = "Empty";
            else
                this.traceName = traceName;
        }
 
        internal override string EventId { get { return BuildEventId(traceName); } }
    }
 
    internal static class SecurityTraceRecordHelper
    {
        internal static void TraceServiceNameBindingOnServer(string serviceBindingNameSentByClient, string defaultServiceBindingNameOfServer, ServiceNameCollection serviceNameCollectionConfiguredOnServer)
        {
            TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ServiceBindingCheck, SR.GetString(SR.TraceCodeServiceBindingCheck), new ServiceBindingNameTraceRecord(serviceBindingNameSentByClient, defaultServiceBindingNameOfServer, serviceNameCollectionConfiguredOnServer), null, null);
        }
 
        internal static void TraceChannelBindingInformation(ExtendedProtectionPolicyHelper policyHelper, bool isServer, ChannelBinding channelBinding)
        {
            TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ChannelBindingCheck, SR.GetString(SR.TraceCodeChannelBindingCheck), new ChannelBindingNameTraceRecord(policyHelper, isServer, channelBinding), null, null);
        }
 
        class ServiceBindingNameTraceRecord : SecurityTraceRecord
        {
            string serviceBindingNameSentByClient;
            string defaultServiceBindingNameOfServer;
            ServiceNameCollection serviceNameCollectionConfiguredOnServer;
 
            public ServiceBindingNameTraceRecord(string serviceBindingNameSentByClient, string defaultServiceBindingNameOfServer, ServiceNameCollection serviceNameCollectionConfiguredOnServer)
                : base("ServiceBindingCheckAfterSpNego")
            {
                this.serviceBindingNameSentByClient = serviceBindingNameSentByClient;
                this.defaultServiceBindingNameOfServer = defaultServiceBindingNameOfServer;
                this.serviceNameCollectionConfiguredOnServer = serviceNameCollectionConfiguredOnServer;
            }
 
            internal override void WriteTo(XmlWriter xml)
            {
                if (xml == null)
                    return;
 
                xml.WriteComment(SR.GetString(SR.ServiceNameFromClient));
                xml.WriteElementString("ServiceName", this.serviceBindingNameSentByClient);
 
                xml.WriteComment(SR.GetString(SR.ServiceNameOnService));
                xml.WriteStartElement("ServiceNameCollection");
                if (this.serviceNameCollectionConfiguredOnServer == null || this.serviceNameCollectionConfiguredOnServer.Count < 1)
                {
                    xml.WriteElementString("ServiceName", this.defaultServiceBindingNameOfServer);
                }
                else
                {
                    foreach (string serviceName in this.serviceNameCollectionConfiguredOnServer)
                    {
                        xml.WriteElementString("ServiceName", serviceName);
                    }
                }
 
                xml.WriteFullEndElement();
 
            }
        }
 
        class ChannelBindingNameTraceRecord : SecurityTraceRecord
        {
            ExtendedProtectionPolicyHelper policyHelper;
            bool isServer;
            bool channelBindingUsed;
            ChannelBinding channelBinding;
 
            public ChannelBindingNameTraceRecord(ExtendedProtectionPolicyHelper policyHelper, bool isServer, ChannelBinding channelBinding)
                : base("SpNegoChannelBindingInformation")
            {
                this.policyHelper = policyHelper;
                this.isServer = isServer;
                this.channelBindingUsed = false;
                this.channelBinding = channelBinding;
            }
 
            internal override void WriteTo(XmlWriter xml)
            {
                if (xml == null)
                    return;
                if (this.policyHelper != null)
                {
                    xml.WriteElementString("PolicyEnforcement", this.policyHelper.PolicyEnforcement.ToString());
                    xml.WriteElementString("ProtectionScenario", this.policyHelper.ProtectionScenario.ToString());
 
                    xml.WriteStartElement("ServiceNameCollection");
 
                    if (this.policyHelper.ServiceNameCollection != null && this.policyHelper.ServiceNameCollection.Count > 0)
                    {
                        foreach (string serviceName in this.policyHelper.ServiceNameCollection)
                        {
                            xml.WriteElementString("ServiceName", serviceName);
                        }
                    }
 
                    xml.WriteFullEndElement();
 
                    if (this.isServer)
                    {
                        this.channelBindingUsed = this.policyHelper.ShouldAddChannelBindingToASC();
                    }
                    else
                    {
                        this.channelBindingUsed = this.policyHelper.ChannelBinding != null;
                    }
 
                    xml.WriteElementString("ChannelBindingUsed", this.channelBindingUsed.ToString());
 
                    if (this.channelBinding != null && this.policyHelper.PolicyEnforcement != PolicyEnforcement.Never && this.channelBindingUsed == true)
                    {
                        ExtendedProtectionPolicy extendedProtection = new ExtendedProtectionPolicy(policyHelper.PolicyEnforcement, channelBinding);
                        xml.WriteElementString("ChannelBindingData", GetBase64EncodedChannelBindingData(extendedProtection));
                    }
                }
                else
                {
                    // This is the case for KerberosRequestorSecurityToken where policyHelper is null.
                    if (this.channelBinding != null)
                    {
                        xml.WriteElementString("ChannelBindingUsed", "true");
 
                        // We do not know the PolicyEnforcement value here on the client side and we can not pass Never 
                        //as ExtendedProtectionPolicy constructor would throw on PolicyEnforcement.Never
                        ExtendedProtectionPolicy extendedProtection = new ExtendedProtectionPolicy(PolicyEnforcement.WhenSupported, channelBinding);
                        xml.WriteElementString("ChannelBindingData", GetBase64EncodedChannelBindingData(extendedProtection));
                    }
                    else
                    {
                        xml.WriteElementString("ChannelBindingUsed", "false");
                        xml.WriteElementString("ChannelBindingData", null);
                    }
                }
 
            }
 
            internal string GetBase64EncodedChannelBindingData(ExtendedProtectionPolicy extendedProtectionPolicy)
            {
                MemoryStream ms = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, extendedProtectionPolicy);
                byte[] channelBindingData = ms.GetBuffer();
                return Convert.ToBase64String(channelBindingData, Base64FormattingOptions.None);
            }
        }
 
        /// <summary>
        /// Used to serialize a token to the trace.  Used in multiple places.
        /// </summary>
        internal class TokenTraceRecord : SecurityTraceRecord
        {
            const string ElementName = "TokenTraceRecord";
 
            SecurityToken _securityToken;
 
            public TokenTraceRecord(SecurityToken securityToken)
                : base(ElementName)
            {
                _securityToken = securityToken;
            }
 
            void WriteSessionToken(XmlWriter writer, SessionSecurityToken sessionToken)
            {
                SessionSecurityTokenHandler ssth = GetOrCreateSessionSecurityTokenHandler();
 
                XmlDictionaryWriter dictionaryWriter = XmlDictionaryWriter.CreateDictionaryWriter(writer);
                ssth.WriteToken(dictionaryWriter, sessionToken);
            }
 
            private static SessionSecurityTokenHandler GetOrCreateSessionSecurityTokenHandler()
            {
                SecurityTokenHandlerCollection defaultHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
                SessionSecurityTokenHandler ssth = defaultHandlers[typeof(SessionSecurityToken)] as SessionSecurityTokenHandler;
 
                if (ssth == null)
                {
                    ssth = new SessionSecurityTokenHandler();
                    defaultHandlers.AddOrReplace(ssth);
                }
 
                return ssth;
            }
 
            internal override void WriteTo(XmlWriter writer)
            {
                writer.WriteStartElement(ElementName);
                writer.WriteAttributeString(DiagnosticStrings.NamespaceTag, EventId);
 
                writer.WriteStartElement("SecurityToken");
                writer.WriteAttributeString("Type", _securityToken.GetType().ToString());
 
                if (_securityToken is SessionSecurityToken)
                {
                    WriteSessionToken(writer, _securityToken as SessionSecurityToken);
                }
                else
                {
                    SecurityTokenHandlerCollection sthc = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
                    if (sthc.CanWriteToken(_securityToken))
                    {
                        {
                            sthc.WriteToken(writer, _securityToken);
                        }
                    }
                    else
                    {
                        writer.WriteElementString("Warning", SR.GetString(SR.TraceUnableToWriteToken, _securityToken.GetType().ToString()));
                    }
                }
 
                writer.WriteEndElement();
            }
        }
    }
}