File: System\ServiceModel\Channels\ChannelBindingUtility.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System;
    using System.Collections;
    using System.Threading;
    using System.Net;
    using System.Net.Security;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Authentication.ExtendedProtection.Configuration;
 
    static class ChannelBindingUtility
    {
        static ExtendedProtectionPolicy disabledPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
        static ExtendedProtectionPolicy defaultPolicy = disabledPolicy;
 
        public static ExtendedProtectionPolicy DisabledPolicy
        {
            get
            {
                return disabledPolicy;
            }
        }
 
        public static ExtendedProtectionPolicy DefaultPolicy
        {
            get
            {
                return defaultPolicy;
            }
        }
 
        public static bool IsDefaultPolicy(ExtendedProtectionPolicy policy)
        {
            return Object.ReferenceEquals(policy, defaultPolicy);
        }
 
        public static void CopyFrom(ExtendedProtectionPolicyElement source, ExtendedProtectionPolicyElement destination)
        {
            destination.PolicyEnforcement = source.PolicyEnforcement;
            destination.ProtectionScenario = source.ProtectionScenario;
            destination.CustomServiceNames.Clear();
            foreach (ServiceNameElement sourceEntry in source.CustomServiceNames)
            {
                ServiceNameElement entry = new ServiceNameElement();
                entry.Name = sourceEntry.Name;
                destination.CustomServiceNames.Add(entry);
            }
        }
 
        public static void InitializeFrom(ExtendedProtectionPolicy source, ExtendedProtectionPolicyElement destination)
        {
            if (!IsDefaultPolicy(source))
            {
                destination.PolicyEnforcement = source.PolicyEnforcement;
                destination.ProtectionScenario = source.ProtectionScenario;
                destination.CustomServiceNames.Clear();
 
                if (source.CustomServiceNames != null)
                {
                    foreach (string name in source.CustomServiceNames)
                    {
                        ServiceNameElement entry = new ServiceNameElement();
                        entry.Name = name;
                        destination.CustomServiceNames.Add(entry);
                    }
                }
            }
        }
 
        public static ExtendedProtectionPolicy BuildPolicy(ExtendedProtectionPolicyElement configurationPolicy)
        {
            //using this pattern allows us to have a different default policy
            //than the NCL team chooses.
            if (configurationPolicy.ElementInformation.IsPresent)
            {
                return configurationPolicy.BuildPolicy();
            }
            else
            {
                return ChannelBindingUtility.DefaultPolicy;
            }
        }
 
        public static ChannelBinding GetToken(SslStream stream)
        {
            return GetToken(stream.TransportContext);
        }
 
        public static ChannelBinding GetToken(TransportContext context)
        {
            ChannelBinding token = null;
            if (context != null)
            {
                token = context.GetChannelBinding(ChannelBindingKind.Endpoint);
            }
            return token;
        }
 
        public static ChannelBinding DuplicateToken(ChannelBinding source)
        {
            if (source == null)
            {
                return null;
            }
 
            return DuplicatedChannelBinding.CreateCopy(source);
        }
 
        public static void TryAddToMessage(ChannelBinding channelBindingToken, Message message, bool messagePropertyOwnsCleanup)
        {
            if (channelBindingToken != null)
            {
                ChannelBindingMessageProperty property = new ChannelBindingMessageProperty(channelBindingToken, messagePropertyOwnsCleanup);
                property.AddTo(message);
                property.Dispose(); //message.Properties.Add() creates a copy...
            }
        }
 
        //does not validate the ExtendedProtectionPolicy.CustomServiceNames collections on the policies
        public static bool AreEqual(ExtendedProtectionPolicy policy1, ExtendedProtectionPolicy policy2)
        {
            Fx.Assert(policy1 != null, "policy1 param cannot be null");
            Fx.Assert(policy2 != null, "policy2 param cannot be null");
 
            if (policy1.PolicyEnforcement == PolicyEnforcement.Never && policy2.PolicyEnforcement == PolicyEnforcement.Never)
            {
                return true;
            }
 
            if (policy1.PolicyEnforcement != policy2.PolicyEnforcement)
            {
                return false;
            }
 
            if (policy1.ProtectionScenario != policy2.ProtectionScenario)
            {
                return false;
            }
 
            if (policy1.CustomChannelBinding != policy2.CustomChannelBinding)
            {
                return false;
            }
 
            return true;
        }
 
        public static bool IsSubset(ServiceNameCollection primaryList, ServiceNameCollection subset)
        {
            bool result = false;
            if (subset == null || subset.Count == 0)
            {
                result = true;
            }
            else if (primaryList == null || primaryList.Count < subset.Count)
            {
                result = false;
            }
            else
            {
                ServiceNameCollection merged = primaryList.Merge(subset);
 
                //The merge routine only adds an entry if it is unique.
                result = (merged.Count == primaryList.Count);
            }
 
            return result;
        }
 
        public static void Dispose(ref ChannelBinding channelBinding)
        {
            // Explicitly cast to IDisposable to avoid the SecurityException.
            IDisposable disposable = (IDisposable)channelBinding;
            channelBinding = null;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
 
        class DuplicatedChannelBinding : ChannelBinding
        {
            [Fx.Tag.SecurityNote(Critical = "Used when referencing raw native memory")]
            [SecurityCritical]
            int size;
 
            DuplicatedChannelBinding()
            {
 
            }
 
            public override int Size
            {
                [Fx.Tag.SecurityNote(Critical = "Used when referencing raw native memory",
                    Safe = "All inputs are validated during initization")]
                [SecuritySafeCritical]
                get { return this.size; }
            }
 
            [Fx.Tag.SecurityNote(Critical = "Invokes unsafe code.",
                Safe = "Unsafe code is effectively encapsulated, all inputs are validated.")]
            [SecuritySafeCritical]
            internal static ChannelBinding CreateCopy(ChannelBinding source)
            {
                Fx.Assert(source != null, "source ChannelBinding should have been checked for null previously");
 
                if (source.IsInvalid || source.IsClosed)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(source.GetType().FullName));
                }
 
                if (source.Size <= 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("source.Size", source.Size,
                        SR.GetString(SR.ValueMustBePositive)));
                }
 
                //Instantiate the SafeHandle before trying to allocate the native memory
                DuplicatedChannelBinding duplicate = new DuplicatedChannelBinding();
 
                //allocate the native memory and make a deep copy of the original.
                duplicate.Initialize(source);
 
                return duplicate;
            }
 
            [Fx.Tag.SecurityNote(Critical = "Contains unsafe code.")]
            [SecurityCritical]
            unsafe void Initialize(ChannelBinding source)
            {
                //allocates the memory pointed to by this.handle
                //and sets this.size after allocation succeeds.
                AllocateMemory(source.Size);
 
                byte* sourceBuffer = (byte*)source.DangerousGetHandle().ToPointer();
                byte* destinationBuffer = (byte*)this.handle.ToPointer();
 
                for (int i = 0; i < source.Size; i++)
                {
                    destinationBuffer[i] = sourceBuffer[i];
                }
 
                this.size = source.Size;
            }
 
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            void AllocateMemory(int bytesToAllocate)
            {
                Fx.Assert(bytesToAllocate > 0, "bytesToAllocate must be positive");
 
                //this protects us from problems like an appdomain shutdown occuring 
                //after allocating the native memory but before the handle gets set (which would result in a memory leak)
                RuntimeHelpers.PrepareConstrainedRegions();
                try { }
                finally
                {
                    base.SetHandle(Marshal.AllocHGlobal(bytesToAllocate));
                }
            }
 
            protected override bool ReleaseHandle()
            {
                Marshal.FreeHGlobal(this.handle);
                base.SetHandle(IntPtr.Zero);
                return true;
            }
        }
    }
}