File: System\IdentityModel\SecurityUtils.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.IdentityModel
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.IdentityModel.Claims;
    using System.IdentityModel.Diagnostics;
    using System.IdentityModel.Policy;
    using System.IdentityModel.Tokens;
    using System.Runtime;
    using System.Security;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Xml;
    using Microsoft.Win32;
 
    static class SecurityUtils
    {
        public const string Identities = "Identities";
        static int fipsAlgorithmPolicy = -1;
        public const int WindowsVistaMajorNumber = 6;
        static IIdentity anonymousIdentity;
 
        // these should be kept in sync with IIS70
        public const string AuthTypeNTLM = "NTLM";
        public const string AuthTypeNegotiate = "Negotiate";
        public const string AuthTypeKerberos = "Kerberos";
        public const string AuthTypeAnonymous = "";
        public const string AuthTypeCertMap = "SSL/PCT"; // mapped from a cert
        public const string AuthTypeBasic = "Basic"; //LogonUser
 
        internal static IIdentity AnonymousIdentity
        {
            get
            {
                if (anonymousIdentity == null)
                    anonymousIdentity = SecurityUtils.CreateIdentity(String.Empty);
                return anonymousIdentity;
            }
        }
 
        public static DateTime MaxUtcDateTime
        {
            get
            {
                // + and -  TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow.
                return new DateTime(DateTime.MaxValue.Ticks - TimeSpan.TicksPerDay, DateTimeKind.Utc);
            }
        }
 
        public static DateTime MinUtcDateTime
        {
            get
            {
                // + and -  TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow.
                return new DateTime(DateTime.MinValue.Ticks + TimeSpan.TicksPerDay, DateTimeKind.Utc);
            }
        }
 
        internal static IIdentity CreateIdentity(string name, string authenticationType)
        {
            return new GenericIdentity(name, authenticationType);
        }
 
        internal static IIdentity CreateIdentity(string name)
        {
            return new GenericIdentity(name);
        }
 
        internal static byte[] CloneBuffer(byte[] buffer)
        {
            return CloneBuffer(buffer, 0, buffer.Length);
        }
 
        internal static byte[] CloneBuffer(byte[] buffer, int offset, int len)
        {
            DiagnosticUtility.DebugAssert(offset >= 0, "Negative offset passed to CloneBuffer.");
            DiagnosticUtility.DebugAssert(len >= 0, "Negative len passed to CloneBuffer.");
            DiagnosticUtility.DebugAssert(buffer.Length - offset >= len, "Invalid parameters to CloneBuffer.");
 
            byte[] copy = DiagnosticUtility.Utility.AllocateByteArray(len);
            Buffer.BlockCopy(buffer, offset, copy, 0, len);
            return copy;
        }
 
        internal static ReadOnlyCollection<SecurityKey> CreateSymmetricSecurityKeys( byte[] key )
        {
            List<SecurityKey> temp = new List<SecurityKey>( 1 );
            temp.Add( new InMemorySymmetricSecurityKey( key ) );
            return temp.AsReadOnly();
        }
 
        internal static byte[] EncryptKey(SecurityToken wrappingToken, string encryptionMethod, byte[] keyToWrap)
        {
            SecurityKey wrappingSecurityKey = null;
            if (wrappingToken.SecurityKeys != null)
            {
                for (int i = 0; i < wrappingToken.SecurityKeys.Count; ++i)
                {
                    if (wrappingToken.SecurityKeys[i].IsSupportedAlgorithm(encryptionMethod))
                    {
                        wrappingSecurityKey = wrappingToken.SecurityKeys[i];
                        break;
                    }
                }
            }
            if (wrappingSecurityKey == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.CannotFindMatchingCrypto, encryptionMethod));
            }
            return wrappingSecurityKey.EncryptKey(encryptionMethod, keyToWrap);
        }
 
        internal static bool MatchesBuffer(byte[] src, byte[] dst)
        {
            return MatchesBuffer(src, 0, dst, 0);
        }
 
        internal static bool MatchesBuffer(byte[] src, int srcOffset, byte[] dst, int dstOffset)
        {
            DiagnosticUtility.DebugAssert(dstOffset >= 0, "Negative dstOffset passed to MatchesBuffer.");
            DiagnosticUtility.DebugAssert(srcOffset >= 0, "Negative srcOffset passed to MatchesBuffer.");
 
            // defensive programming
            if ((dstOffset < 0) || (srcOffset < 0))
                return false;
 
            if (src == null || srcOffset >= src.Length)
                return false;
            if (dst == null || dstOffset >= dst.Length)
                return false;
            if ((src.Length - srcOffset) != (dst.Length - dstOffset))
                return false;
 
            for (int i = srcOffset, j = dstOffset; i < src.Length; i++, j++)
            {
                if (src[i] != dst[j])
                    return false;
            }
            return true;
        }
 
        internal static string GetCertificateId(X509Certificate2 certificate)
        {
            string certificateId = certificate.SubjectName.Name;
            if (string.IsNullOrEmpty(certificateId))
                certificateId = certificate.Thumbprint;
            return certificateId;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls critical method X509Certificate2.Reset.",
            Safe = "Per review from CLR security team, this method does nothing unsafe.")]
        [SecuritySafeCritical]
        internal static void ResetCertificate(X509Certificate2 certificate)
        {
            certificate.Reset();
        }
 
        internal static bool IsCurrentlyTimeEffective(DateTime effectiveTime, DateTime expirationTime, TimeSpan maxClockSkew)
        {
            DateTime curEffectiveTime = (effectiveTime < DateTime.MinValue.Add(maxClockSkew)) ? effectiveTime : effectiveTime.Subtract(maxClockSkew);
            DateTime curExpirationTime = (expirationTime > DateTime.MaxValue.Subtract(maxClockSkew)) ? expirationTime : expirationTime.Add(maxClockSkew);
            DateTime curTime = DateTime.UtcNow;
 
            return (curEffectiveTime.ToUniversalTime() <= curTime) && (curTime < curExpirationTime.ToUniversalTime());
        }
 
        // Federal Information Processing Standards Publications
        // at http://www.itl.nist.gov/fipspubs/geninfo.htm
        internal static bool RequiresFipsCompliance
        {
            [Fx.Tag.SecurityNote(Critical = "Calls an UnsafeNativeMethod and a Critical method (GetFipsAlgorithmPolicyKeyFromRegistry.",
                Safe = "processes the return and just returns a bool, which is safe.")]
            [SecuritySafeCritical]
            get
            {
                if (fipsAlgorithmPolicy == -1)
                {
                    if (Environment.OSVersion.Version.Major >= WindowsVistaMajorNumber)
                    {
                        bool fipsEnabled;
#pragma warning suppress 56523 // we check for the return code of the method instead of calling GetLastWin32Error
                        bool readPolicy = (CAPI.S_OK == CAPI.BCryptGetFipsAlgorithmMode(out fipsEnabled));
 
                        if (readPolicy && fipsEnabled)
                            fipsAlgorithmPolicy = 1;
                        else
                            fipsAlgorithmPolicy = 0;
                    }
                    else
                    {
                        fipsAlgorithmPolicy = GetFipsAlgorithmPolicyKeyFromRegistry();
                        if (fipsAlgorithmPolicy != 1)
                            fipsAlgorithmPolicy = 0;
                    }
                }
                return fipsAlgorithmPolicy == 1;
            }
        }
 
        const string fipsPolicyRegistryKey = @"System\CurrentControlSet\Control\Lsa";
 
        /// <SecurityNote>
        /// Critical - Asserts to get a value from the registry
        /// </SecurityNote>
        [SecurityCritical]
        [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\" + fipsPolicyRegistryKey)]
        static int GetFipsAlgorithmPolicyKeyFromRegistry()
        {
            int fipsAlgorithmPolicy = -1;
            using (RegistryKey fipsAlgorithmPolicyKey = Registry.LocalMachine.OpenSubKey(fipsPolicyRegistryKey, false))
            {
                if (fipsAlgorithmPolicyKey != null)
                {
                    object data = fipsAlgorithmPolicyKey.GetValue("FIPSAlgorithmPolicy");
                    if (data != null)
                        fipsAlgorithmPolicy = (int)data;
                }
            }
            return fipsAlgorithmPolicy;
        }
 
        private static bool s_readMaxTransformsPerReference = false;
        private static long s_maxTransformsPerReference = 10;
 
        [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
        [SecuritySafeCritical]
        internal static long GetMaxXmlTransformsPerReference()
        {
            // Allow machine administrators to specify a maximum number of Transforms per Reference in SignedXML.
            if (!s_readMaxTransformsPerReference)
            {
                s_maxTransformsPerReference = GetNetFxSecurityRegistryValue("SignedXmlMaxTransformsPerReference", s_maxTransformsPerReference);
                Thread.MemoryBarrier();
                s_readMaxTransformsPerReference = true;
            }
 
            return s_maxTransformsPerReference;
        }
 
        private static bool s_readMaxReferencesPerSignedInfo = false;
        private static long s_maxReferencesPerSignedInfo = 100;
 
        [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
        [SecuritySafeCritical]
        internal static long GetMaxXmlReferencesPerSignedInfo()
        {
            // Allow machine administrators to specify a maximum number of References per SignedInfo/Signature in SignedXML.
            if (!s_readMaxReferencesPerSignedInfo)
            {
                s_maxReferencesPerSignedInfo = GetNetFxSecurityRegistryValue("SignedXmlMaxReferencesPerSignedInfo", s_maxReferencesPerSignedInfo);
                Thread.MemoryBarrier();
                s_readMaxReferencesPerSignedInfo = true;
            }
 
            return s_maxReferencesPerSignedInfo;
        }
 
        private static long GetNetFxSecurityRegistryValue(string regValueName, long defaultValue)
        {
            try
            {
                using (RegistryKey securityRegKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework\Security", false))
                {
                    if (securityRegKey != null)
                    {
                        object regValue = securityRegKey.GetValue(regValueName);
                        if (regValue != null)
                        {
                            RegistryValueKind valueKind = securityRegKey.GetValueKind(regValueName);
                            if (valueKind == RegistryValueKind.DWord || valueKind == RegistryValueKind.QWord)
                            {
                                return Convert.ToInt64(regValue, CultureInfo.InvariantCulture);
                            }
                        }
                    }
                }
            }
            catch (SecurityException) 
            { 
                // we could not open the key - that's fine, we can proceed with the default value 
            }
 
            return defaultValue;
        }
 
        /// <summary>
        /// Checks if an <see cref="X509Certificate2Collection"/> object contains a given <see cref="X509Certificate2"/> 
        /// by comparing <see cref="X509Certificate2.RawData"/>, byte-by-byte.
        /// </summary>
        /// <param name="collection">An <see cref="X509Certificate2Collection"/> object.</param>
        /// <param name="certificate">A <see cref="X509Certificate2"/> object.</param>
        /// <returns>true if <paramref name="collection"/> contains <paramref name="collection"/>, false otherwise.</returns>
        internal static bool CollectionContainsCertificate(X509Certificate2Collection collection, X509Certificate2 certificate)
        {
            if (collection == null || certificate == null || certificate.Handle == IntPtr.Zero)
                return false;
 
            var certificateRawData = certificate.RawData;
 
            for (int i = 0; i < collection.Count; i++)
            {
                if (collection[i].Handle == IntPtr.Zero)
                    continue;
 
                var memberCertificateRawData = collection[i].RawData;
 
                if (CryptoHelper.IsEqual(memberCertificateRawData, certificateRawData))
                    return true;
            }
 
            return false;
        }
 
        class SimpleAuthorizationContext : AuthorizationContext
        {
            SecurityUniqueId id;
            UnconditionalPolicy policy;
            IDictionary<string, object> properties;
 
            public SimpleAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)
            {
                this.policy = (UnconditionalPolicy)authorizationPolicies[0];
                Dictionary<string, object> properties = new Dictionary<string, object>();
                if (this.policy.PrimaryIdentity != null && this.policy.PrimaryIdentity != SecurityUtils.AnonymousIdentity)
                {
                    List<IIdentity> identities = new List<IIdentity>();
                    identities.Add(this.policy.PrimaryIdentity);
                    properties.Add(SecurityUtils.Identities, identities);
                }
                // Might need to port ReadOnlyDictionary?
                this.properties = properties;
            }
 
            public override string Id
            {
                get
                {
                    if (this.id == null)
                        this.id = SecurityUniqueId.Create();
                    return this.id.Value;
                }
            }
            public override ReadOnlyCollection<ClaimSet> ClaimSets { get { return this.policy.Issuances; } }
            public override DateTime ExpirationTime { get { return this.policy.ExpirationTime; } }
            public override IDictionary<string, object> Properties { get { return this.properties; } }
        }
 
        internal static AuthorizationContext CreateDefaultAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)
        {
            AuthorizationContext authorizationContext;
            // This is faster than Policy evaluation.
            if (authorizationPolicies != null && authorizationPolicies.Count == 1 && authorizationPolicies[0] is UnconditionalPolicy)
            {
                authorizationContext = new SimpleAuthorizationContext(authorizationPolicies);
            }
            // degenerate case
            else if (authorizationPolicies == null || authorizationPolicies.Count <= 0)
            {
                return DefaultAuthorizationContext.Empty;
            }
            else
            {
                // there are some policies, run them until they are all done
                DefaultEvaluationContext evaluationContext = new DefaultEvaluationContext();
                object[] policyState = new object[authorizationPolicies.Count];
                object done = new object();
 
                int oldContextCount;
                do
                {
                    oldContextCount = evaluationContext.Generation;
 
                    for (int i = 0; i < authorizationPolicies.Count; i++)
                    {
                        if (policyState[i] == done)
                            continue;
 
                        IAuthorizationPolicy policy = authorizationPolicies[i];
                        if (policy == null)
                        {
                            policyState[i] = done;
                            continue;
                        }
 
                        if (policy.Evaluate(evaluationContext, ref policyState[i]))
                        {
                            policyState[i] = done;
 
                            if (DiagnosticUtility.ShouldTraceVerbose)
                            {
                                TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.AuthorizationPolicyEvaluated,
                                    SR.GetString(SR.AuthorizationPolicyEvaluated, policy.Id));
                            }
                        }
                    }
 
                } while (oldContextCount < evaluationContext.Generation);
 
                authorizationContext = new DefaultAuthorizationContext(evaluationContext);
            }
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AuthorizationContextCreated,
                    SR.GetString(SR.AuthorizationContextCreated, authorizationContext.Id));
            }
 
            return authorizationContext;
        }
 
        internal static string ClaimSetToString(ClaimSet claimSet)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("ClaimSet [");
            for (int i = 0; i < claimSet.Count; i++)
            {
                Claim claim = claimSet[i];
                if (claim != null)
                {
                    sb.Append("  ");
                    sb.AppendLine(claim.ToString());
                }
            }
            string prefix = "] by ";
            ClaimSet issuer = claimSet;
            do
            {
                // PreSharp Bug: A null-dereference can occur here.
#pragma warning suppress 56506 // issuer was just set to this.
                issuer = issuer.Issuer;
                sb.AppendFormat("{0}{1}", prefix, issuer == claimSet ? "Self" : (issuer.Count <= 0 ? "Unknown" : issuer[0].ToString()));
                prefix = " -> ";
            } while (issuer.Issuer != issuer);
            return sb.ToString();
        }
 
        // This is the workaround, Since store.Certificates returns a full collection
        // of certs in store.  These are holding native resources.
        internal static void ResetAllCertificates(X509Certificate2Collection certificates)
        {
            if (certificates != null)
            {
                for (int i = 0; i < certificates.Count; ++i)
                {
                    ResetCertificate(certificates[i]);
                }
            }
        }
 
        internal static byte[] ReadContentAsBase64(XmlDictionaryReader reader, long maxBufferSize)
        {
            if (reader == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
 
            // Code cloned from System.Xml.XmlDictionaryReder.
            byte[][] buffers = new byte[32][];
            byte[] buffer;
            // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when converting text
            int count = 384;
            int bufferCount = 0;
            int totalRead = 0;
            while (true)
            {
                buffer = new byte[count];
                buffers[bufferCount++] = buffer;
                int read = 0;
                while (read < buffer.Length)
                {
                    int actual = reader.ReadContentAsBase64(buffer, read, buffer.Length - read);
                    if (actual == 0)
                        break;
                    read += actual;
                }
                if (totalRead > maxBufferSize - read)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new LimitExceededException(SR.GetString(SR.BufferQuotaExceededReadingBase64, maxBufferSize)));
                totalRead += read;
                if (read < buffer.Length)
                    break;
                count = count * 2;
            }
            buffer = new byte[totalRead];
            int offset = 0;
            for (int i = 0; i < bufferCount - 1; i++)
            {
                Buffer.BlockCopy(buffers[i], 0, buffer, offset, buffers[i].Length);
                offset += buffers[i].Length;
            }
            Buffer.BlockCopy(buffers[bufferCount - 1], 0, buffer, offset, totalRead - offset);
            return buffer;
        }
 
        internal static byte[] DecryptKey(SecurityToken unwrappingToken, string encryptionMethod, byte[] wrappedKey, out SecurityKey unwrappingSecurityKey)
        {
            unwrappingSecurityKey = null;
            if (unwrappingToken.SecurityKeys != null)
            {
                for (int i = 0; i < unwrappingToken.SecurityKeys.Count; ++i)
                {
                    if (unwrappingToken.SecurityKeys[i].IsSupportedAlgorithm(encryptionMethod))
                    {
                        unwrappingSecurityKey = unwrappingToken.SecurityKeys[i];
                        break;
                    }
                }
            }
            if (unwrappingSecurityKey == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityMessageSerializationException(SR.GetString(SR.CannotFindMatchingCrypto, encryptionMethod)));
            }
            return unwrappingSecurityKey.DecryptKey(encryptionMethod, wrappedKey);
        }
 
        public static bool TryCreateX509CertificateFromRawData(byte[] rawData, out X509Certificate2 certificate)
        {
            certificate = (rawData == null || rawData.Length == 0) ? null : new X509Certificate2(rawData);
            return certificate != null && certificate.Handle != IntPtr.Zero;
        }
 
        internal static byte[] DecodeHexString(string hexString)
        {
            hexString = hexString.Trim();
 
            bool spaceSkippingMode = false;
 
            int i = 0;
            int length = hexString.Length;
 
            if ((length >= 2) &&
                (hexString[0] == '0') &&
                ((hexString[1] == 'x') || (hexString[1] == 'X')))
            {
                length = hexString.Length - 2;
                i = 2;
            }
 
            if (length < 2)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
 
            byte[] sArray;
 
            if (length >= 3 && hexString[i + 2] == ' ')
            {
                if (length % 3 != 2)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
 
                spaceSkippingMode = true;
 
                // Each hex digit will take three spaces, except the first (hence the plus 1).
                sArray = DiagnosticUtility.Utility.AllocateByteArray(length / 3 + 1);
            }
            else
            {
                if (length % 2 != 0)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
 
                spaceSkippingMode = false;
 
                // Each hex digit will take two spaces
                sArray = DiagnosticUtility.Utility.AllocateByteArray(length / 2);
            }
 
            int digit;
            int rawdigit;
            for (int j = 0; i < hexString.Length; i += 2, j++)
            {
                rawdigit = ConvertHexDigit(hexString[i]);
                digit = ConvertHexDigit(hexString[i + 1]);
                sArray[j] = (byte)(digit | (rawdigit << 4));
                if (spaceSkippingMode)
                    i++;
            }
            return (sArray);
        }
 
        static int ConvertHexDigit(Char val)
        {
            if (val <= '9' && val >= '0')
                return (val - '0');
            else if (val >= 'a' && val <= 'f')
                return ((val - 'a') + 10);
            else if (val >= 'A' && val <= 'F')
                return ((val - 'A') + 10);
            else
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
        }
 
        internal static ReadOnlyCollection<IAuthorizationPolicy> CreateAuthorizationPolicies(ClaimSet claimSet)
        {
            return CreateAuthorizationPolicies(claimSet, SecurityUtils.MaxUtcDateTime);
        }
 
        internal static ReadOnlyCollection<IAuthorizationPolicy> CreateAuthorizationPolicies(ClaimSet claimSet, DateTime expirationTime)
        {
            List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
            policies.Add(new UnconditionalPolicy(claimSet, expirationTime));
            return policies.AsReadOnly();
        }
 
        internal static string GenerateId()
        {
            return SecurityUniqueId.Create().Value;
        }
 
        internal static bool IsSupportedAlgorithm(string algorithm, SecurityToken token)
        {
            if (token.SecurityKeys == null)
            {
                return false;
            }
            for (int i = 0; i < token.SecurityKeys.Count; ++i)
            {
                if (token.SecurityKeys[i].IsSupportedAlgorithm(algorithm))
                {
                    return true;
                }
            }
            return false;
        }
 
        internal static IIdentity CloneIdentityIfNecessary(IIdentity identity)
        {
            if (identity != null)
            {
                WindowsIdentity wid = identity as WindowsIdentity;
                if (wid != null)
                {
                    return CloneWindowsIdentityIfNecessary(wid);
                }
                //X509Identity x509 = identity as X509Identity;
                //if (x509 != null)
                //{
                //    return x509.Clone();
                //}
            }
            return identity;
        }
 
        /// <SecurityNote>
        /// Critical - calls two critical methods: UnsafeGetWindowsIdentityToken and UnsafeCreateWindowsIdentityFromToken
        /// Safe - "clone" operation is considered safe despite using WindowsIdentity IntPtr token
        ///        must not let IntPtr token leak in or out
        /// </SecurityNote>
        [SecuritySafeCritical]
        internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid)
        {
            return CloneWindowsIdentityIfNecessary(wid, wid.AuthenticationType);
        }
 
        [SecuritySafeCritical]
        internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid, string authenticationType)
        {
 
            if (wid != null)
            {
                IntPtr token = UnsafeGetWindowsIdentityToken(wid);
                if (token != IntPtr.Zero)
                {
                    return UnsafeCreateWindowsIdentityFromToken(token, authenticationType);
                }
            }
            return wid;
        }
 
        /// <SecurityNote>
        /// Critical - elevates in order to return the WindowsIdentity.Token property
        ///            caller must protect return value
        /// </SecurityNote>
        [SecurityCritical]
        [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
        static IntPtr UnsafeGetWindowsIdentityToken(WindowsIdentity wid)
        {
            return wid.Token;
        }
 
        /// <SecurityNote>
        /// Critical - elevates in order to construct a WindowsIdentity instance from an IntPtr
        ///            caller must protect parameter return value
        /// </SecurityNote>
        // We pass the authenticationType in as WindowsIdentity will all into a priviledged call in LSA which could fail
        // resulting in a null authenticationType.
        [SecurityCritical]
        [SecurityPermission(SecurityAction.Assert, ControlPrincipal = true, UnmanagedCode = true)]
        static WindowsIdentity UnsafeCreateWindowsIdentityFromToken(IntPtr token, string authenticationType)
        {
            if (authenticationType != null)
            {
                return new WindowsIdentity(token, authenticationType);
            }
            else
            {
                return new WindowsIdentity(token);
            }
        }
 
        internal static ClaimSet CloneClaimSetIfNecessary(ClaimSet claimSet)
        {
            if (claimSet != null)
            {
                WindowsClaimSet wic = claimSet as WindowsClaimSet;
                if (wic != null)
                {
                    return wic.Clone();
                }
                //X509CertificateClaimSet x509 = claimSet as X509CertificateClaimSet;
                //if (x509 != null)
                //{
                //    return x509.Clone();
                //}
            }
            return claimSet;
        }
 
        internal static ReadOnlyCollection<ClaimSet> CloneClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)
        {
            if (claimSets != null)
            {
                bool clone = false;
                for (int i = 0; i < claimSets.Count; ++i)
                {
                    if (claimSets[i] is WindowsClaimSet)// || claimSets[i] is X509CertificateClaimSet)
                    {
                        clone = true;
                        break;
                    }
                }
                if (clone)
                {
                    List<ClaimSet> ret = new List<ClaimSet>(claimSets.Count);
                    for (int i = 0; i < claimSets.Count; ++i)
                    {
                        ret.Add(SecurityUtils.CloneClaimSetIfNecessary(claimSets[i]));
                    }
                    return ret.AsReadOnly();
                }
            }
            return claimSets;
        }
 
        internal static void DisposeClaimSetIfNecessary(ClaimSet claimSet)
        {
            if (claimSet != null)
            {
                SecurityUtils.DisposeIfNecessary(claimSet as WindowsClaimSet);
            }
        }
 
        internal static void DisposeClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)
        {
            if (claimSets != null)
            {
                for (int i = 0; i < claimSets.Count; ++i)
                {
                    SecurityUtils.DisposeIfNecessary(claimSets[i] as WindowsClaimSet);
                }
            }
        }
 
        internal static ReadOnlyCollection<IAuthorizationPolicy> CloneAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
        {
            if (authorizationPolicies != null && authorizationPolicies.Count > 0)
            {
                bool clone = false;
                for (int i = 0; i < authorizationPolicies.Count; ++i)
                {
                    UnconditionalPolicy policy = authorizationPolicies[i] as UnconditionalPolicy;
                    if (policy != null && policy.IsDisposable)
                    {
                        clone = true;
                        break;
                    }
                }
                if (clone)
                {
                    List<IAuthorizationPolicy> ret = new List<IAuthorizationPolicy>(authorizationPolicies.Count);
                    for (int i = 0; i < authorizationPolicies.Count; ++i)
                    {
                        UnconditionalPolicy policy = authorizationPolicies[i] as UnconditionalPolicy;
                        if (policy != null)
                        {
                            ret.Add(policy.Clone());
                        }
                        else
                        {
                            ret.Add(authorizationPolicies[i]);
                        }
                    }
                    return ret.AsReadOnly();
                }
            }
            return authorizationPolicies;
        }
 
        public static void DisposeAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
        {
            if (authorizationPolicies != null && authorizationPolicies.Count > 0)
            {
                for (int i = 0; i < authorizationPolicies.Count; ++i)
                {
                    DisposeIfNecessary(authorizationPolicies[i] as UnconditionalPolicy);
                }
            }
        }
 
        public static void DisposeIfNecessary(IDisposable obj)
        {
            if (obj != null)
            {
                obj.Dispose();
            }
        }
    }
 
    /// <summary>
    /// Internal helper class to help keep Kerberos and Spnego in sync.
    /// This code is shared by: 
    ///     System\IdentityModel\Tokens\KerberosReceiverSecurityToken.cs
    ///     System\ServiceModel\Security\WindowsSspiNegotiation.cs
    /// Both this code paths require this logic.
    /// </summary>
    internal class ExtendedProtectionPolicyHelper
    {
        //
        // keep the defaults: _protectionScenario and _policyEnforcement, in sync with: static class System.ServiceModel.Channel.ChannelBindingUtility
        // We can't access those defaults as IdentityModel cannot take a dependency on ServiceModel
        //
        static ExtendedProtectionPolicy disabledPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
 
        PolicyEnforcement _policyEnforcement;
        ProtectionScenario _protectionScenario;
        ChannelBinding _channelBinding;
        ServiceNameCollection _serviceNameCollection;
        bool _checkServiceBinding;
 
        public ExtendedProtectionPolicyHelper(ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
        {
            _protectionScenario = DefaultPolicy.ProtectionScenario;
            _policyEnforcement = DefaultPolicy.PolicyEnforcement;
 
            _channelBinding = channelBinding;
            _serviceNameCollection = null;
            _checkServiceBinding = true;
 
            if (extendedProtectionPolicy != null)
            {
                _policyEnforcement = extendedProtectionPolicy.PolicyEnforcement;
                _protectionScenario = extendedProtectionPolicy.ProtectionScenario;
                _serviceNameCollection = extendedProtectionPolicy.CustomServiceNames;
            }
 
            if (_policyEnforcement == PolicyEnforcement.Never)
            {
                _checkServiceBinding = false;
            }
        }
 
        public bool ShouldAddChannelBindingToASC()
        {
            return (_channelBinding != null && _policyEnforcement != PolicyEnforcement.Never && _protectionScenario != ProtectionScenario.TrustedProxy);
        }
 
        public ChannelBinding ChannelBinding
        {
            get { return _channelBinding; }
        }
 
        public bool ShouldCheckServiceBinding
        {
            get { return _checkServiceBinding; }
        }
 
        public ServiceNameCollection ServiceNameCollection
        {
            get { return _serviceNameCollection; }
        }
 
        public ProtectionScenario ProtectionScenario
        {
            get { return _protectionScenario; }
        }
 
        public PolicyEnforcement PolicyEnforcement
        {
            get { return _policyEnforcement; }
        }
 
        /// <summary>
        /// ServiceBinding check has the following logic:
        /// 1. Check PolicyEnforcement - never => return true;
        /// 1. Check status returned from SecurityContext which is obtained when querying for the serviceBinding
        /// 2. Check PolicyEnforcement
        ///     a. WhenSupported - valid when OS does not support, null serviceBinding is valid
        ///     b. Always - a non-empty servicebinding must be available
        /// 3. if serviceBinding is non null, check that an expected value is in the ServiceNameCollection - ignoring case
        ///    note that the empty string must be explicitly specified in the serviceNames.
        /// </summary>
        /// <param name="securityContext to ">status Code returned when obtaining serviceBinding from SecurityContext</param>
        /// <returns>If servicebinding is valid</returns>
        public void CheckServiceBinding(SafeDeleteContext securityContext, string defaultServiceBinding)
        {
            if (_policyEnforcement == PolicyEnforcement.Never)
            {
                return;
            }
 
            string serviceBinding = null;
            int statusCode = SspiWrapper.QuerySpecifiedTarget(securityContext, out serviceBinding);
 
            if (statusCode != (int)SecurityStatus.OK)
            {
                // only two acceptable non-zero values
                // client OS not patched: stausCode == TargetUnknown
                // service OS not patched: statusCode == Unsupported
                if (statusCode != (int)SecurityStatus.TargetUnknown && statusCode != (int)SecurityStatus.Unsupported)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
                }
 
                // if policyEnforcement is Always we needed to see a TargetName (SPN)
                if (_policyEnforcement == PolicyEnforcement.Always)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
                }
 
                // in this case we accept because either the client or service is not patched.
                if (_policyEnforcement == PolicyEnforcement.WhenSupported)
                {
                    return;
                }
 
                // guard against futures, force failure and fix as necessary
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
            }
 
            switch (_policyEnforcement)
            {
                case PolicyEnforcement.WhenSupported:
                    // serviceBinding == null => client is not patched
                    if (serviceBinding == null)
                        return;
                    break;
 
                case PolicyEnforcement.Always:
                    // serviceBinding == null => client is not patched 
                    // serviceBinding == "" => SB was not specified
                    if (string.IsNullOrEmpty(serviceBinding))
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
                    break;
            }
 
            // iff no values were 'user' set, then check the defaultServiceBinding
            if (_serviceNameCollection == null || _serviceNameCollection.Count < 1)
            {
                if (defaultServiceBinding == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
 
                if (string.Compare(defaultServiceBinding, serviceBinding, StringComparison.OrdinalIgnoreCase) == 0)
                    return;
 
                if (string.IsNullOrEmpty(serviceBinding))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
                else
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, serviceBinding)));
            }
 
            if (_serviceNameCollection != null)
            {
                if (_serviceNameCollection.Contains(serviceBinding))
                {
                    return;
                }
            }
 
            if (string.IsNullOrEmpty(serviceBinding))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
            else
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, serviceBinding)));
        }
 
        /// <summary>
        /// Keep this in sync with \System\ServiceModel\Channels\ChannelBindingUtility.cs
        /// </summary>
        public static ExtendedProtectionPolicy DefaultPolicy
        {   //
            //keep the default in sync with : static class System.ServiceModel.Channels.ChannelBindingUtility
            //we can't use these defaults as IdentityModel cannot take a dependency on ServiceModel
            //
 
            // Current POR is "Never" respect the above note.
 
            get { return disabledPolicy; }
        }
    }
 
    static class EmptyReadOnlyCollection<T>
    {
        public static ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new List<T>());
    }
}