558 lines
24 KiB
C#
558 lines
24 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Dispatcher
|
||
|
{
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.ComponentModel;
|
||
|
using System.Diagnostics;
|
||
|
using System.IdentityModel.Claims;
|
||
|
using System.IdentityModel.Policy;
|
||
|
using System.IdentityModel.Tokens;
|
||
|
using System.Runtime;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Security;
|
||
|
using System.Security.Principal;
|
||
|
using System.ServiceModel;
|
||
|
using System.ServiceModel.Activation;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Diagnostics;
|
||
|
using System.ServiceModel.Security;
|
||
|
using System.ServiceModel.Security.Tokens;
|
||
|
using System.Text;
|
||
|
using System.Threading;
|
||
|
using ClaimsIdentity = System.Security.Claims.ClaimsIdentity;
|
||
|
using ClaimsPrincipal = System.Security.Claims.ClaimsPrincipal;
|
||
|
using EXTENDED_NAME_FORMAT = System.ServiceModel.ComIntegration.EXTENDED_NAME_FORMAT;
|
||
|
using SafeCloseHandle = System.IdentityModel.SafeCloseHandle;
|
||
|
using SafeNativeMethods = System.ServiceModel.ComIntegration.SafeNativeMethods;
|
||
|
using Win32Error = System.ServiceModel.ComIntegration.Win32Error;
|
||
|
|
||
|
internal sealed class SecurityImpersonationBehavior
|
||
|
{
|
||
|
PrincipalPermissionMode principalPermissionMode;
|
||
|
object roleProvider;
|
||
|
bool impersonateCallerForAllOperations;
|
||
|
Dictionary<string, string> 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<string, string>(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 (AspNetEnvironment.Current.RequiresImpersonation)
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
ClaimsPrincipal claimsPrincipal = OperationContext.Current.ClaimsPrincipal;
|
||
|
|
||
|
if (this.principalPermissionMode == PrincipalPermissionMode.UseWindowsGroups)
|
||
|
{
|
||
|
principal = ( claimsPrincipal is WindowsPrincipal ) ? claimsPrincipal : GetWindowsPrincipal( securityContext );
|
||
|
}
|
||
|
else if (this.principalPermissionMode == PrincipalPermissionMode.UseAspNetRoles)
|
||
|
{
|
||
|
principal = new RoleProviderPrincipal(this.roleProvider, securityContext);
|
||
|
}
|
||
|
else if (this.principalPermissionMode == PrincipalPermissionMode.Custom)
|
||
|
{
|
||
|
principal = GetCustomPrincipal(securityContext);
|
||
|
}
|
||
|
else if (this.principalPermissionMode == PrincipalPermissionMode.Always)
|
||
|
{
|
||
|
principal = claimsPrincipal ?? new ClaimsPrincipal( new ClaimsIdentity() );
|
||
|
}
|
||
|
|
||
|
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)));
|
||
|
}
|
||
|
|
||
|
internal bool IsSecurityContextImpersonationRequired(ref MessageRpc rpc)
|
||
|
{
|
||
|
return ((rpc.Operation.Impersonation == ImpersonationOption.Required)
|
||
|
|| ((rpc.Operation.Impersonation == ImpersonationOption.Allowed) && this.impersonateCallerForAllOperations));
|
||
|
}
|
||
|
|
||
|
internal bool IsImpersonationEnabledOnCurrentOperation(ref MessageRpc rpc)
|
||
|
{
|
||
|
return this.IsSecurityContextImpersonationRequired(ref rpc) ||
|
||
|
AspNetEnvironment.Current.RequiresImpersonation ||
|
||
|
this.principalPermissionMode != PrincipalPermissionMode.None;
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(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 || AspNetEnvironment.Current.RequiresImpersonation)
|
||
|
{
|
||
|
impersonationContext = StartImpersonation2(ref rpc, securityContext, isSecurityContextImpersonationOn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method HostedImpersonationContext.Impersonate."
|
||
|
+ "Caller must ensure that this method is called at an appropriate time and that 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, SecurityUtils.AuthTypeKerberos))
|
||
|
{
|
||
|
impersonationContext = windowsIdentity.Impersonate();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
throw TraceUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityContextDoesNotAllowImpersonation, rpc.Operation.Action)), rpc.Request);
|
||
|
}
|
||
|
else if (AspNetEnvironment.Current.RequiresImpersonation)
|
||
|
{
|
||
|
if (rpc.HostingProperty != null)
|
||
|
{
|
||
|
impersonationContext = rpc.HostingProperty.Impersonate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SecurityTraceRecordHelper.TraceImpersonationSucceeded(rpc.EventTraceActivity, 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 (Fx.IsFatal(ex))
|
||
|
{
|
||
|
throw;
|
||
|
}
|
||
|
SecurityTraceRecordHelper.TraceImpersonationFailed(rpc.EventTraceActivity, 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 (Fx.IsFatal(auditException))
|
||
|
throw;
|
||
|
|
||
|
DiagnosticUtility.TraceHandledException(auditException, TraceEventType.Error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
throw;
|
||
|
}
|
||
|
|
||
|
return impersonationContext;
|
||
|
}
|
||
|
|
||
|
public void StopImpersonation(ref MessageRpc rpc, IDisposable impersonationContext, IPrincipal originalPrincipal, bool isThreadPrincipalSet)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (IsSecurityContextImpersonationRequired(ref rpc) || AspNetEnvironment.Current.RequiresImpersonation)
|
||
|
{
|
||
|
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();
|
||
|
System.ServiceModel.Diagnostics.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.
|
||
|
Fx.Assert(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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|