SecurityImpersonationBehavior.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / ServiceModel / System / ServiceModel / Dispatcher / SecurityImpersonationBehavior.cs / 2 / SecurityImpersonationBehavior.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace System.ServiceModel.Dispatcher 
{
    using System; 
    using System.ServiceModel.Channels; 
    using System.ServiceModel;
    using System.ServiceModel.Description; 
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics; 
    using System.Security;
    using System.Security.Principal; 
    using System.Security.Cryptography; 
    using System.Threading;
    using System.IdentityModel.Claims; 
    using System.IdentityModel.Policy;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.ServiceModel.Diagnostics; 
    using System.ServiceModel.Security;
    using System.Text; 
 
    using System.ServiceModel.Security.Tokens;
    using SafeNativeMethods = System.ServiceModel.ComIntegration.SafeNativeMethods; 
    using Win32Error = System.ServiceModel.ComIntegration.Win32Error;
    using EXTENDED_NAME_FORMAT = System.ServiceModel.ComIntegration.EXTENDED_NAME_FORMAT;
    using SafeCloseHandle = System.IdentityModel.SafeCloseHandle;
 
    internal sealed class SecurityImpersonationBehavior
    { 
        PrincipalPermissionMode principalPermissionMode; 
        object roleProvider;
        bool impersonateCallerForAllOperations; 
        Dictionary domainNameMap;
        Random random;
        const int maxDomainNameMapSize = 5;
 
        static WindowsPrincipal anonymousWindowsPrincipal;
        AuditLevel auditLevel = ServiceSecurityAuditBehavior.defaultMessageAuthenticationAuditLevel; 
        AuditLogLocation auditLogLocation = ServiceSecurityAuditBehavior.defaultAuditLogLocation; 
        bool suppressAuditFailure = ServiceSecurityAuditBehavior.defaultSuppressAuditFailure;
 
        SecurityImpersonationBehavior(DispatchRuntime dispatch)
        {
            this.principalPermissionMode = dispatch.PrincipalPermissionMode;
            this.impersonateCallerForAllOperations = dispatch.ImpersonateCallerForAllOperations; 
            this.auditLevel = dispatch.MessageAuthenticationAuditLevel;
            this.auditLogLocation = dispatch.SecurityAuditLogLocation; 
            this.suppressAuditFailure = dispatch.SuppressAuditFailure; 
            if (dispatch.IsRoleProviderSet)
            { 
                ApplyRoleProvider(dispatch);
            }
            this.domainNameMap = new Dictionary(maxDomainNameMapSize, StringComparer.OrdinalIgnoreCase);
        } 

        public static SecurityImpersonationBehavior CreateIfNecessary(DispatchRuntime dispatch) 
        { 
            if (IsSecurityBehaviorNeeded(dispatch))
            { 
                return new SecurityImpersonationBehavior(dispatch);
            }
            else
            { 
                return null;
            } 
        } 

        static WindowsPrincipal AnonymousWindowsPrincipal 
        {
            get
            {
                if (anonymousWindowsPrincipal == null) 
                    anonymousWindowsPrincipal = new WindowsPrincipal(WindowsIdentity.GetAnonymous());
 
                return anonymousWindowsPrincipal; 
            }
        } 

        [MethodImpl(MethodImplOptions.NoInlining)]
        void ApplyRoleProvider(DispatchRuntime dispatch)
        { 
            this.roleProvider = dispatch.RoleProvider;
        } 
 
        static bool IsSecurityBehaviorNeeded(DispatchRuntime dispatch)
        { 
            if (IsSecurityContextImpersonationRequiredByAspNet())
            {
                return true;
            } 

            if (dispatch.PrincipalPermissionMode != PrincipalPermissionMode.None) 
            { 
                return true;
            } 

            // Impersonation behavior is required if
            // 1) Contract requires it or
            // 2) Contract allows it and config requires it 
            for (int i = 0; i < dispatch.Operations.Count; i++)
            { 
                DispatchOperation operation = dispatch.Operations[i]; 

                if (operation.Impersonation == ImpersonationOption.Required) 
                {
                    return true;
                }
                else if (operation.Impersonation == ImpersonationOption.NotAllowed) 
                {
                    // a validation rule enforces that config cannot require impersonation in this case 
                    return false; 
                }
            } 
            // contract allows impersonation. Return true if config requires it.
            return dispatch.ImpersonateCallerForAllOperations;
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        IPrincipal SetCurrentThreadPrincipal(ServiceSecurityContext securityContext, out bool isThreadPrincipalSet) 
        { 
            IPrincipal result = null;
            IPrincipal principal = null; 
            if (this.principalPermissionMode == PrincipalPermissionMode.UseWindowsGroups)
            {
                principal = GetWindowsPrincipal(securityContext);
            } 
            else if (this.principalPermissionMode == PrincipalPermissionMode.UseAspNetRoles)
            { 
                principal = new RoleProviderPrincipal(this.roleProvider, securityContext); 
            }
            else if (this.principalPermissionMode == PrincipalPermissionMode.Custom) 
            {
                principal = GetCustomPrincipal(securityContext);
            }
 
            if (principal != null)
            { 
                result = Thread.CurrentPrincipal; 
                Thread.CurrentPrincipal = principal;
                isThreadPrincipalSet = true; 
            }
            else
            {
                isThreadPrincipalSet = false; 
            }
 
            return result; 
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        static IPrincipal GetCustomPrincipal(ServiceSecurityContext securityContext)
        {
            object customPrincipal; 
            if (securityContext.AuthorizationContext.Properties.TryGetValue(SecurityUtils.Principal, out customPrincipal) && customPrincipal is IPrincipal)
                return (IPrincipal)customPrincipal; 
            else 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.NoPrincipalSpecifiedInAuthorizationContext)));
        } 

        bool IsSecurityContextImpersonationRequired(ref MessageRpc rpc)
        {
            return ((rpc.Operation.Impersonation == ImpersonationOption.Required) 
                || ((rpc.Operation.Impersonation == ImpersonationOption.Allowed) && this.impersonateCallerForAllOperations));
        } 
 
        static bool IsSecurityContextImpersonationRequiredByAspNet()
        { 
            return ServiceHostingEnvironment.IsHosted && ServiceHostingEnvironment.AspNetCompatibilityEnabled;
        }

        ///  
        /// Critical - calls SecurityCritical method StartImpersonation2
        ///            caller must ensure that this method is called at an appropriate time and that impersonationContext out param is Dispose()'d correctly 
        ///  
        [SecurityCritical]
        public void StartImpersonation(ref MessageRpc rpc, out IDisposable impersonationContext, out IPrincipal originalPrincipal, out bool isThreadPrincipalSet) 
        {
            impersonationContext = null;
            originalPrincipal = null;
            isThreadPrincipalSet = false; 
            ServiceSecurityContext securityContext;
            bool setThreadPrincipal = this.principalPermissionMode != PrincipalPermissionMode.None; 
            bool isSecurityContextImpersonationOn = IsSecurityContextImpersonationRequired(ref rpc); 
            if (setThreadPrincipal || isSecurityContextImpersonationOn)
                securityContext = GetAndCacheSecurityContext(ref rpc); 
            else
                securityContext = null;

            if (setThreadPrincipal && securityContext != null) 
                originalPrincipal = this.SetCurrentThreadPrincipal(securityContext, out isThreadPrincipalSet);
 
            if (isSecurityContextImpersonationOn || IsSecurityContextImpersonationRequiredByAspNet()) 
            {
                impersonationContext = StartImpersonation2(ref rpc, securityContext, isSecurityContextImpersonationOn); 
            }
        }

        ///  
        /// Critical - calls SecurityCritical method HostedImpersonationContext.Impersonate
        ///            caller must ensure that this method is called at an appropriate time and that the result is Dispose()'d correctly 
        ///  
        [SecurityCritical]
        IDisposable StartImpersonation2(ref MessageRpc rpc, ServiceSecurityContext securityContext, bool isSecurityContextImpersonationOn) 
        {
            IDisposable impersonationContext = null;
            try
            { 
                if (isSecurityContextImpersonationOn)
                { 
                    if (securityContext == null) 
                        throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxSecurityContextPropertyMissingFromRequestMessage)), rpc.Request);
 
                    WindowsIdentity impersonationToken = securityContext.WindowsIdentity;
                    if (impersonationToken.User != null)
                    {
                        impersonationContext = impersonationToken.Impersonate(); 
                    }
                    else if (securityContext.PrimaryIdentity is WindowsSidIdentity) 
                    { 
                        WindowsSidIdentity sidIdentity = (WindowsSidIdentity)securityContext.PrimaryIdentity;
                        if (sidIdentity.SecurityIdentifier.IsWellKnown(WellKnownSidType.AnonymousSid)) 
                        {
                            impersonationContext = new WindowsAnonymousIdentity().Impersonate();
                        }
                        else 
                        {
                            string fullyQualifiedDomainName = GetUpnFromDownlevelName(sidIdentity.Name); 
                            using (WindowsIdentity windowsIdentity = new WindowsIdentity(fullyQualifiedDomainName)) 
                            {
                                impersonationContext = windowsIdentity.Impersonate(); 
                            }
                        }
                    }
                    else 
                        throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityContextDoesNotAllowImpersonation, rpc.Operation.Action)), rpc.Request);
                } 
                else if (IsSecurityContextImpersonationRequiredByAspNet()) 
                {
                    if (rpc.HostingProperty != null && rpc.HostingProperty.ImpersonationContext != null) 
                    {
                        impersonationContext = rpc.HostingProperty.ImpersonationContext.Impersonate();
                    }
                } 

                SecurityTraceRecordHelper.TraceImpersonationSucceeded(rpc.Operation); 
 
                // update the impersonation succeed audit
                if (AuditLevel.Success == (this.auditLevel & AuditLevel.Success)) 
                {
                    SecurityAuditHelper.WriteImpersonationSuccessEvent(this.auditLogLocation,
                        this.suppressAuditFailure, rpc.Operation.Name, SecurityUtils.GetIdentityNamesFromContext(securityContext.AuthorizationContext));
                } 
            }
            catch (Exception ex) 
            { 
                if (DiagnosticUtility.IsFatal(ex))
                { 
                    throw;
                }
                SecurityTraceRecordHelper.TraceImpersonationFailed(rpc.Operation, ex);
 
                //
                // Update the impersonation failure audit 
                // Copy SecurityAuthorizationBehavior.Audit level to here!!! 
                //
                if (AuditLevel.Failure == (this.auditLevel & AuditLevel.Failure)) 
                {
                    try
                    {
                        string primaryIdentity; 
                        if ( securityContext != null)
                            primaryIdentity = SecurityUtils.GetIdentityNamesFromContext(securityContext.AuthorizationContext); 
                        else 
                            primaryIdentity = SecurityUtils.AnonymousIdentity.Name;
 
                        SecurityAuditHelper.WriteImpersonationFailureEvent(this.auditLogLocation,
                            this.suppressAuditFailure, rpc.Operation.Name, primaryIdentity, ex);
                    }
#pragma warning suppress 56500 
                    catch (Exception auditException)
                    { 
                        if (DiagnosticUtility.IsFatal(auditException)) 
                            throw;
 
                        DiagnosticUtility.ExceptionUtility.TraceHandledException(auditException, TraceEventType.Error);
                    }
                }
 
                throw;
            } 
 
            return impersonationContext;
        } 

        public void StopImpersonation(ref MessageRpc rpc, IDisposable impersonationContext, IPrincipal originalPrincipal, bool isThreadPrincipalSet)
        {
            try 
            {
                if (IsSecurityContextImpersonationRequired(ref rpc) || IsSecurityContextImpersonationRequiredByAspNet()) 
                { 
                    if (impersonationContext != null)
                    { 
                        impersonationContext.Dispose();
                    }
                }
 
                if (isThreadPrincipalSet)
                { 
                    Thread.CurrentPrincipal = originalPrincipal; 
                }
            } 
#pragma warning suppress 56500 // covered by FxCOP
            catch
            {
                string message = null; 
                try
                { 
                    message = SR.GetString(SR.SFxRevertImpersonationFailed0); 
                }
                finally 
                {
                    DiagnosticUtility.FailFast(message);
                }
            } 
        }
 
        IPrincipal GetWindowsPrincipal(ServiceSecurityContext securityContext) 
        {
            WindowsIdentity wid = securityContext.WindowsIdentity; 
            if (!wid.IsAnonymous)
                return new WindowsPrincipal(wid);

            WindowsSidIdentity wsid = securityContext.PrimaryIdentity as WindowsSidIdentity; 
            if (wsid != null)
                return new WindowsSidPrincipal(wsid, securityContext); 
 
            return AnonymousWindowsPrincipal;
        } 

        ServiceSecurityContext GetAndCacheSecurityContext(ref MessageRpc rpc)
        {
            ServiceSecurityContext securityContext = rpc.SecurityContext; 

            if (!rpc.HasSecurityContext) 
            { 
                SecurityMessageProperty securityContextProperty = rpc.Request.Properties.Security;
                if (securityContextProperty == null) 
                    securityContext = null; // SecurityContext.Anonymous
                else
                {
                    securityContext = securityContextProperty.ServiceSecurityContext; 
                    if (securityContext == null)
                        throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityContextMissing, rpc.Operation.Name)), rpc.Request); 
                } 

                rpc.SecurityContext = securityContext; 
                rpc.HasSecurityContext = true;
            }

            return securityContext; 
        }
 
        string GetUpnFromDownlevelName(string downlevelName) 
        {
            if (downlevelName == null) 
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("downlevelName");
            }
            int delimiterPos = downlevelName.IndexOf('\\'); 
            if ((delimiterPos < 0) || (delimiterPos == 0) || (delimiterPos == downlevelName.Length - 1))
            { 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.DownlevelNameCannotMapToUpn, downlevelName))); 
            }
            string shortDomainName = downlevelName.Substring(0, delimiterPos + 1); 
            string userName = downlevelName.Substring(delimiterPos + 1);
            string fullDomainName;
            bool found;
 
            // 1) Read from cache
            lock (this.domainNameMap) 
            { 
                found = this.domainNameMap.TryGetValue(shortDomainName, out fullDomainName);
            } 

            // 2) Not found, do expensive look up
            if (!found)
            { 
                uint capacity = 50;
                StringBuilder fullyQualifiedDomainName = new StringBuilder((int)capacity); 
                if (!SafeNativeMethods.TranslateName(shortDomainName, EXTENDED_NAME_FORMAT.NameSamCompatible, EXTENDED_NAME_FORMAT.NameCanonical, 
                    fullyQualifiedDomainName, out capacity))
                { 
                    int errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == (int)Win32Error.ERROR_INSUFFICIENT_BUFFER)
                    {
                        fullyQualifiedDomainName = new StringBuilder((int)capacity); 
                        if (!SafeNativeMethods.TranslateName(shortDomainName, EXTENDED_NAME_FORMAT.NameSamCompatible, EXTENDED_NAME_FORMAT.NameCanonical,
                            fullyQualifiedDomainName, out capacity)) 
                        { 
                            errorCode = Marshal.GetLastWin32Error();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.DownlevelNameCannotMapToUpn, downlevelName), new Win32Exception(errorCode))); 
                        }
                    }
                    else
                    { 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR.GetString(SR.DownlevelNameCannotMapToUpn, downlevelName), new Win32Exception(errorCode)));
                    } 
                } 
                // trim the trailing / from fqdn
                fullyQualifiedDomainName = fullyQualifiedDomainName.Remove(fullyQualifiedDomainName.Length - 1, 1); 
                fullDomainName = fullyQualifiedDomainName.ToString();

                // 3) Save in cache (remove a random item if cache is full)
                lock (this.domainNameMap) 
                {
                    if (this.domainNameMap.Count >= maxDomainNameMapSize) 
                    { 
                        if (this.random == null)
                        { 
                            this.random = new Random(unchecked((int)DateTime.Now.Ticks));
                        }
                        int victim = this.random.Next() % this.domainNameMap.Count;
                        foreach (string key in this.domainNameMap.Keys) 
                        {
                            if (victim <= 0) 
                            { 
                                this.domainNameMap.Remove(key);
                                break; 
                            }
                            --victim;
                        }
                    } 
                    this.domainNameMap[shortDomainName] = fullDomainName;
                } 
            } 
            return userName + "@" + fullDomainName;
        } 


        class WindowsSidPrincipal : IPrincipal
        { 
            WindowsSidIdentity identity;
            ServiceSecurityContext securityContext; 
 
            public WindowsSidPrincipal(WindowsSidIdentity identity, ServiceSecurityContext securityContext)
            { 
                this.identity = identity;
                this.securityContext = securityContext;
            }
 
            public IIdentity Identity
            { 
                get { return this.identity; } 
            }
 
            public bool IsInRole(string role)
            {
                if (role == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("role"); 

                NTAccount account = new NTAccount(role); 
                Claim claim = Claim.CreateWindowsSidClaim((SecurityIdentifier)account.Translate(typeof(SecurityIdentifier))); 
                AuthorizationContext authContext = this.securityContext.AuthorizationContext;
                for (int i = 0; i < authContext.ClaimSets.Count; i++) 
                {
                    ClaimSet claimSet = authContext.ClaimSets[i];
                    if (claimSet.ContainsClaim(claim))
                        return true; 
                }
                return false; 
            } 
        }
 
        class WindowsAnonymousIdentity
        {
            public IDisposable Impersonate()
            { 
                // PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
#pragma warning suppress 56523 // The LastWin32Error can be ignored here. 
                IntPtr threadHandle = SafeNativeMethods.GetCurrentThread(); 
                SafeCloseHandle tokenHandle;
                if (!SafeNativeMethods.OpenCurrentThreadToken(threadHandle, TokenAccessLevels.Impersonate, true, out tokenHandle)) 
                {
                    int error = Marshal.GetLastWin32Error();
                    Utility.CloseInvalidOutSafeHandle(tokenHandle);
                    if (error == (int)System.ServiceModel.ComIntegration.Win32Error.ERROR_NO_TOKEN) 
                    {
                        tokenHandle = new SafeCloseHandle(IntPtr.Zero, false); 
                    } 
                    else
                    { 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error));
                    }
                }
 
                if (!SafeNativeMethods.ImpersonateAnonymousUserOnCurrentThread(threadHandle))
                { 
                    int error = Marshal.GetLastWin32Error(); 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error));
                } 

                return new ImpersonationContext(threadHandle, tokenHandle);
            }
 
            class ImpersonationContext : IDisposable
            { 
                IntPtr threadHandle; 
                SafeCloseHandle tokenHandle;
                bool disposed = false; 

                public ImpersonationContext(IntPtr threadHandle, SafeCloseHandle tokenHandle)
                {
                    this.threadHandle = threadHandle; 
                    this.tokenHandle = tokenHandle;
                } 
 
                void Undo()
                { 
                    // PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
#pragma warning suppress 56523 // The LastWin32Error can be ignored here.
                    DiagnosticUtility.DebugAssert(this.threadHandle == SafeNativeMethods.GetCurrentThread(), "");
                    // We are in the Dispose method. If a failure occurs we just have to ignore it. 
                    // PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
                    // #pragma warning suppress 56523 // The LastWin32Error can be ignored here. 
                    if (!SafeNativeMethods.SetCurrentThreadToken(IntPtr.Zero, this.tokenHandle)) 
                    {
                        int error = Marshal.GetLastWin32Error(); 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityException(SR.GetString(SR.RevertImpersonationFailure,
                            new Win32Exception(error).Message)));
                    }
                    tokenHandle.Close(); 
                }
 
                public void Dispose() 
                {
                    if (!this.disposed) 
                    {
                        Undo();
                    }
                    this.disposed = true; 
                }
            } 
        } 
    }
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK