File: System\ServiceModel\Activation\HostedImpersonationContext.cs
Project: ndp\cdf\src\WCF\System.ServiceModel.Activation\System.ServiceModel.Activation.csproj (System.ServiceModel.Activation)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Activation
{
    using System.ComponentModel;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Principal;
    using System.ServiceModel.Activation.Interop;
    using System.Threading;
 
    [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - All member methods are security critical. The class might be used outside of the restricted SecurityContext." +
        "Ensure they do not accidentally satisfy any demands.")]
    class HostedImpersonationContext
    {
        [Fx.Tag.SecurityNote(Critical = "Stores the impersonation token handle. Since we're allowing 'safe' Impersonation of the token we got from asp.net we need to protect this value.")]
        [SecurityCritical]
        SafeCloseHandleCritical tokenHandle;
 
        [Fx.Tag.SecurityNote(Critical = "Controls lifetime of token handle, caller must use care.")]
        [SecurityCritical]
        int refCount = 0;
 
        [Fx.Tag.SecurityNote(Critical = "Security critical field, caller must use care.")]
        [SecurityCritical]
        bool isImpersonated;
 
        [Fx.Tag.SecurityNote(Critical = "Calls two safe native methods under OpenCurrentThreadTokenCritical: GetCurrentThread and OpenThreadToken." +
            "Marshal.GetLastWin32Error captures current thread token in a SecurityCritical field.")]
        [SecurityCritical]
        public HostedImpersonationContext()
        {
            if (ServiceHostingEnvironment.AspNetCompatibilityEnabled)
            {
                int error;
                bool isSuccess = SafeNativeMethods.OpenCurrentThreadTokenCritical(TokenAccessLevels.Query | TokenAccessLevels.Impersonate,
                    true, out tokenHandle, out error);
 
                if (isSuccess)
                {
                    isImpersonated = true;
                    Interlocked.Increment(ref refCount);
                }
                else
                {
                    CloseInvalidOutSafeHandleCritical(tokenHandle);
                    tokenHandle = null;
                    if (error != SafeNativeMethods.ERROR_NO_TOKEN)
                    {
                        throw FxTrace.Exception.AsError(new Win32Exception(error, SR.Hosting_ImpersonationFailed));
                    }
                }
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls SetHandleAsInvalid which has a LinkDemand for UnmanagedCode.")]
        [SecurityCritical]
        static void CloseInvalidOutSafeHandleCritical(SafeHandle handle)
        {
            // Workaround for 64-bit CLR bug VSWhidbey 546830 - sometimes invalid SafeHandles come back null.
            if (handle != null)
            {
                Fx.Assert(handle.IsInvalid, "CloseInvalidOutSafeHandle called with a valid handle!");
 
                // Calls SuppressFinalize.
                handle.SetHandleAsInvalid();
            }
        }
 
 
        public bool IsImpersonated
        {
            [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical isImpersonated field.", Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
            [SecuritySafeCritical]
            get
            {
                return isImpersonated;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical tokenHandle field and uses LinkDemanded DangerousGetHandle method as well as UnsafeCreate." +
            "Caller should use with care, must take responsibility for reverting impersonation.")]
        [SecurityCritical]
        public IDisposable Impersonate()
        {
            if (!isImpersonated)
                return null;
 
            Fx.Assert(tokenHandle != null, "The token handle was incorrectly released earlier.");
            HostedInnerImpersonationContext context = null;
            lock (tokenHandle)
            {
                context = HostedInnerImpersonationContext.UnsafeCreate(tokenHandle.DangerousGetHandle());
                GC.KeepAlive(tokenHandle);
            }
            return context;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Controls lifetime of token handle, caller must use care.")]
        [SecurityCritical]
        public void AddRef()
        {
            if (IsImpersonated)
            {
                Interlocked.Increment(ref refCount);
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Controls lifetime of token handle, caller must use care.")]
        [SecurityCritical]
        public void Release()
        {
            if (IsImpersonated)
            {
                Fx.Assert(tokenHandle != null, "The token handle is incorrectly released earlier.");
                int currentCount = Interlocked.Decrement(ref refCount);
                if (currentCount == 0)
                {
                    lock (tokenHandle)
                    {
                        tokenHandle.Close();
                        tokenHandle = null;
                    }
                }
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Keeps track of impersonated user, caller must use with care and call Dispose at the appropriate time.")]
#pragma warning disable 618 // have not moved to the v4 security model yet
        [SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
        class HostedInnerImpersonationContext : IDisposable
        {
            IDisposable impersonatedContext;
 
            HostedInnerImpersonationContext(IDisposable impersonatedContext)
            {
                if (impersonatedContext == null)
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Hosting_ImpersonationFailed));
                }
                this.impersonatedContext = impersonatedContext;
            }
 
            public static HostedInnerImpersonationContext UnsafeCreate(IntPtr token)
            {
                return new HostedInnerImpersonationContext(HostingEnvironmentWrapper.UnsafeImpersonate(token));
            }
 
            public void Dispose()
            {
                if (impersonatedContext != null)
                {
                    impersonatedContext.Dispose();
                    impersonatedContext = null;
                }
            }
        }
    }
}