/mcs/class/referencesource/mscorlib/system/security/accesscontrol/privilege.cs
C# | 734 lines | 566 code | 95 blank | 73 comment | 101 complexity | 4bf218666e4a940cd45ccf87901ed455 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- // ==++==
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // ==--==
- /*============================================================
- **
- ** Class: Privilege
- **
- ** Purpose: Managed wrapper for NT privileges.
- **
- ** Date: July 1, 2004
- **
- ===========================================================*/
- using Microsoft.Win32;
- using Microsoft.Win32.SafeHandles;
- using System.Collections;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Runtime.ConstrainedExecution;
- using System.Security.Permissions;
- using System.Security.Principal;
- using System.Threading;
- using System.Runtime.Versioning;
- using System.Diagnostics.Contracts;
- namespace System.Security.AccessControl
- {
- using CultureInfo = System.Globalization.CultureInfo;
- using FCall = System.Security.Principal.Win32;
- using Luid = Microsoft.Win32.Win32Native.LUID;
- #if false
- internal delegate void PrivilegedHelper();
- #endif
- internal sealed class Privilege
- {
- private static LocalDataStoreSlot tlsSlot = Thread.AllocateDataSlot();
- private static Hashtable privileges = new Hashtable();
- private static Hashtable luids = new Hashtable();
- private static ReaderWriterLock privilegeLock = new ReaderWriterLock();
- private bool needToRevert = false;
- private bool initialState = false;
- private bool stateWasChanged = false;
- [System.Security.SecurityCritical] // auto-generated
- private Luid luid;
- private readonly Thread currentThread = Thread.CurrentThread;
- private TlsContents tlsContents = null;
- public const string CreateToken = "SeCreateTokenPrivilege";
- public const string AssignPrimaryToken = "SeAssignPrimaryTokenPrivilege";
- public const string LockMemory = "SeLockMemoryPrivilege";
- public const string IncreaseQuota = "SeIncreaseQuotaPrivilege";
- public const string UnsolicitedInput = "SeUnsolicitedInputPrivilege";
- public const string MachineAccount = "SeMachineAccountPrivilege";
- public const string TrustedComputingBase = "SeTcbPrivilege";
- public const string Security = "SeSecurityPrivilege";
- public const string TakeOwnership = "SeTakeOwnershipPrivilege";
- public const string LoadDriver = "SeLoadDriverPrivilege";
- public const string SystemProfile = "SeSystemProfilePrivilege";
- public const string SystemTime = "SeSystemtimePrivilege";
- public const string ProfileSingleProcess = "SeProfileSingleProcessPrivilege";
- public const string IncreaseBasePriority = "SeIncreaseBasePriorityPrivilege";
- public const string CreatePageFile = "SeCreatePagefilePrivilege";
- public const string CreatePermanent = "SeCreatePermanentPrivilege";
- public const string Backup = "SeBackupPrivilege";
- public const string Restore = "SeRestorePrivilege";
- public const string Shutdown = "SeShutdownPrivilege";
- public const string Debug = "SeDebugPrivilege";
- public const string Audit = "SeAuditPrivilege";
- public const string SystemEnvironment = "SeSystemEnvironmentPrivilege";
- public const string ChangeNotify = "SeChangeNotifyPrivilege";
- public const string RemoteShutdown = "SeRemoteShutdownPrivilege";
- public const string Undock = "SeUndockPrivilege";
- public const string SyncAgent = "SeSyncAgentPrivilege";
- public const string EnableDelegation = "SeEnableDelegationPrivilege";
- public const string ManageVolume = "SeManageVolumePrivilege";
- public const string Impersonate = "SeImpersonatePrivilege";
- public const string CreateGlobal = "SeCreateGlobalPrivilege";
- public const string TrustedCredentialManagerAccess = "SeTrustedCredManAccessPrivilege";
- public const string ReserveProcessor = "SeReserveProcessorPrivilege";
- //
- // This routine is a wrapper around a hashtable containing mappings
- // of privilege names to LUIDs
- //
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- private static Luid LuidFromPrivilege( string privilege )
- {
- Luid luid;
- luid.LowPart = 0;
- luid.HighPart = 0;
- //
- // Look up the privilege LUID inside the cache
- //
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- privilegeLock.AcquireReaderLock( -1 );
- if ( luids.Contains( privilege ))
- {
- luid = ( Luid )luids[ privilege ];
- privilegeLock.ReleaseReaderLock();
- }
- else
- {
- privilegeLock.ReleaseReaderLock();
- if ( false == Win32Native.LookupPrivilegeValue( null, privilege, ref luid ))
- {
- int error = Marshal.GetLastWin32Error();
- if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
- {
- throw new OutOfMemoryException();
- }
- else if ( error == Win32Native.ERROR_ACCESS_DENIED )
- {
- throw new UnauthorizedAccessException();
- }
- else if ( error == Win32Native.ERROR_NO_SUCH_PRIVILEGE )
- {
- throw new ArgumentException(
- Environment.GetResourceString( "Argument_InvalidPrivilegeName",
- privilege ));
- }
- else
- {
- Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "LookupPrivilegeValue() failed with unrecognized error code {0}", error ));
- throw new InvalidOperationException();
- }
- }
- privilegeLock.AcquireWriterLock( -1 );
- }
- }
- finally
- {
- if ( privilegeLock.IsReaderLockHeld )
- {
- privilegeLock.ReleaseReaderLock();
- }
- if ( privilegeLock.IsWriterLockHeld )
- {
- if ( !luids.Contains( privilege ))
- {
- luids[ privilege ] = luid;
- privileges[ luid ] = privilege;
- }
- privilegeLock.ReleaseWriterLock();
- }
- }
- return luid;
- }
- private sealed class TlsContents : IDisposable
- {
- private bool disposed = false;
- private int referenceCount = 1;
- [System.Security.SecurityCritical] // auto-generated
- private SafeAccessTokenHandle threadHandle = new SafeAccessTokenHandle( IntPtr.Zero );
- private bool isImpersonating = false;
- [System.Security.SecurityCritical] // auto-generated
- private static volatile SafeAccessTokenHandle processHandle = new SafeAccessTokenHandle( IntPtr.Zero );
- private static readonly object syncRoot = new object();
- #region Constructor and Finalizer
-
- [System.Security.SecuritySafeCritical] // auto-generated
- static TlsContents()
- {
- }
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
- public TlsContents()
- {
- int error = 0;
- int cachingError = 0;
- bool success = true;
- if ( processHandle.IsInvalid)
- {
- lock( syncRoot )
- {
- if ( processHandle.IsInvalid)
- {
- SafeAccessTokenHandle localProcessHandle;
- if ( false == Win32Native.OpenProcessToken(
- Win32Native.GetCurrentProcess(),
- TokenAccessLevels.Duplicate,
- out localProcessHandle))
- {
- cachingError = Marshal.GetLastWin32Error();
- success = false;
- }
- processHandle = localProcessHandle;
- }
- }
- }
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- // Make the sequence non-interruptible
- }
- finally
- {
- try
- {
- //
- // Open the thread token; if there is no thread token, get one from
- // the process token by impersonating self.
- //
- SafeAccessTokenHandle threadHandleBefore = this.threadHandle;
- error = FCall.OpenThreadToken(
- TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
- WinSecurityContext.Process,
- out this.threadHandle );
- unchecked { error &= ~(int)0x80070000; }
- if ( error != 0 )
- {
- if ( success == true )
- {
- this.threadHandle = threadHandleBefore;
- if ( error != Win32Native.ERROR_NO_TOKEN )
- {
- success = false;
- }
- Contract.Assert( this.isImpersonating == false, "Incorrect isImpersonating state" );
- if ( success == true )
- {
- error = 0;
- if ( false == Win32Native.DuplicateTokenEx(
- processHandle,
- TokenAccessLevels.Impersonate | TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
- IntPtr.Zero,
- Win32Native.SECURITY_IMPERSONATION_LEVEL.Impersonation,
- System.Security.Principal.TokenType.TokenImpersonation,
- ref this.threadHandle ))
- {
- error = Marshal.GetLastWin32Error();
- success = false;
- }
- }
- if ( success == true )
- {
- error = FCall.SetThreadToken( this.threadHandle );
- unchecked { error &= ~(int)0x80070000; }
- if ( error != 0 )
- {
- success = false;
- }
- }
- if ( success == true )
- {
- this.isImpersonating = true;
- }
- }
- else
- {
- error = cachingError;
- }
- }
- else
- {
- success = true;
- }
- }
- finally
- {
- if ( !success )
- {
- Dispose();
- }
- }
- }
- if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
- {
- throw new OutOfMemoryException();
- }
- else if ( error == Win32Native.ERROR_ACCESS_DENIED ||
- error == Win32Native.ERROR_CANT_OPEN_ANONYMOUS )
- {
- throw new UnauthorizedAccessException();
- }
- else if ( error != 0 )
- {
- Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "WindowsIdentity.GetCurrentThreadToken() failed with unrecognized error code {0}", error ));
- throw new InvalidOperationException();
- }
- }
- [System.Security.SecuritySafeCritical]
- ~TlsContents()
- {
- if ( !this.disposed )
- {
- Dispose( false );
- }
- }
- #endregion
- #region IDisposable implementation
- [System.Security.SecuritySafeCritical] // overrides public transparent member
- public void Dispose()
- {
- Dispose( true );
- GC.SuppressFinalize( this );
- }
- [System.Security.SecurityCritical] // auto-generated
- private void Dispose( bool disposing )
- {
- if ( this.disposed ) return;
- if ( disposing )
- {
- if ( this.threadHandle != null )
- {
- this.threadHandle.Dispose();
- this.threadHandle = null;
- }
- }
- if ( this.isImpersonating )
- {
- FCall.RevertToSelf();
- }
- this.disposed = true;
- }
- #endregion
- #region Reference Counting
- public void IncrementReferenceCount()
- {
- this.referenceCount++;
- }
- [System.Security.SecurityCritical] // auto-generated
- public int DecrementReferenceCount()
- {
- int result = --this.referenceCount;
- if ( result == 0 )
- {
- Dispose();
- }
- return result;
- }
- public int ReferenceCountValue
- {
- get { return this.referenceCount; }
- }
- #endregion
- #region Properties
- public SafeAccessTokenHandle ThreadHandle
- {
- [System.Security.SecurityCritical] // auto-generated
- get { return this.threadHandle; }
- }
- public bool IsImpersonating
- {
- get { return this.isImpersonating; }
- }
- #endregion
- }
- #region Constructors
- [System.Security.SecurityCritical] // auto-generated
- public Privilege( string privilegeName )
- {
- if ( privilegeName == null )
- {
- throw new ArgumentNullException( "privilegeName" );
- }
- Contract.EndContractBlock();
- this.luid = LuidFromPrivilege( privilegeName );
- }
- #endregion
- //
- // Finalizer simply ensures that the privilege was not leaked
- //
- [System.Security.SecuritySafeCritical]
- ~Privilege()
- {
- Contract.Assert( !this.needToRevert, "Must revert privileges that you alter!" );
- if ( this.needToRevert )
- {
- Revert();
- }
- }
- #region Public interface
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- public void Enable()
- {
- this.ToggleState( true );
- }
- public bool NeedToRevert
- {
- get { return this.needToRevert; }
- }
- #endregion
- // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )]
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain | ResourceScope.Assembly)]
- private void ToggleState( bool enable )
- {
- int error = 0;
- //
- // All privilege operations must take place on the same thread
- //
- if ( !this.currentThread.Equals( Thread.CurrentThread ))
- {
- throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustBeSameThread" ));
- }
- //
- // This privilege was already altered and needs to be reverted before it can be altered again
- //
- if ( this.needToRevert )
- {
- throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustRevertPrivilege" ));
- }
- //
- // Need to make this block of code non-interruptible so that it would preserve
- // consistency of thread oken state even in the face of catastrophic exceptions
- //
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- //
- // The payload is entirely in the finally block
- // This is how we ensure that the code will not be
- // interrupted by catastrophic exceptions
- //
- }
- finally
- {
- try
- {
- //
- // Retrieve TLS state
- //
- this.tlsContents = Thread.GetData( tlsSlot ) as TlsContents;
- if ( this.tlsContents == null )
- {
- this.tlsContents = new TlsContents();
- Thread.SetData( tlsSlot, this.tlsContents );
- }
- else
- {
- this.tlsContents.IncrementReferenceCount();
- }
- Win32Native.TOKEN_PRIVILEGE newState = new Win32Native.TOKEN_PRIVILEGE();
- newState.PrivilegeCount = 1;
- newState.Privilege.Luid = this.luid;
- newState.Privilege.Attributes = enable ? Win32Native.SE_PRIVILEGE_ENABLED : Win32Native.SE_PRIVILEGE_DISABLED;
-
- Win32Native.TOKEN_PRIVILEGE previousState = new Win32Native.TOKEN_PRIVILEGE();
- uint previousSize = 0;
- //
- // Place the new privilege on the thread token and remember the previous state.
- //
- if ( false == Win32Native.AdjustTokenPrivileges(
- this.tlsContents.ThreadHandle,
- false,
- ref newState,
- ( uint )Marshal.SizeOf( previousState ),
- ref previousState,
- ref previousSize ))
- {
- error = Marshal.GetLastWin32Error();
- }
- else if ( Win32Native.ERROR_NOT_ALL_ASSIGNED == Marshal.GetLastWin32Error())
- {
- error = Win32Native.ERROR_NOT_ALL_ASSIGNED;
- }
- else
- {
- //
- // This is the initial state that revert will have to go back to
- //
- this.initialState = (( previousState.Privilege.Attributes & Win32Native.SE_PRIVILEGE_ENABLED ) != 0 );
- //
- // Remember whether state has changed at all
- //
- this.stateWasChanged = ( this.initialState != enable );
- //
- // If we had to impersonate, or if the privilege state changed we'll need to revert
- //
- this.needToRevert = this.tlsContents.IsImpersonating || this.stateWasChanged;
- }
- }
- finally
- {
- if ( !this.needToRevert )
- {
- this.Reset();
- }
- }
- }
- if ( error == Win32Native.ERROR_NOT_ALL_ASSIGNED )
- {
- throw new PrivilegeNotHeldException( privileges[this.luid] as string );
- }
- if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
- {
- throw new OutOfMemoryException();
- }
- else if ( error == Win32Native.ERROR_ACCESS_DENIED ||
- error == Win32Native.ERROR_CANT_OPEN_ANONYMOUS )
- {
- throw new UnauthorizedAccessException();
- }
- else if ( error != 0 )
- {
- Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "AdjustTokenPrivileges() failed with unrecognized error code {0}", error ));
- throw new InvalidOperationException();
- }
- }
- // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )]
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- public void Revert()
- {
- int error = 0;
- if ( !this.currentThread.Equals( Thread.CurrentThread ))
- {
- throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustBeSameThread" ));
- }
- if ( !this.NeedToRevert )
- {
- return;
- }
- //
- // This code must be eagerly prepared and non-interruptible.
- //
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- //
- // The payload is entirely in the finally block
- // This is how we ensure that the code will not be
- // interrupted by catastrophic exceptions
- //
- }
- finally
- {
- bool success = true;
- try
- {
- //
- // Only call AdjustTokenPrivileges if we're not going to be reverting to self,
- // on this Revert, since doing the latter obliterates the thread token anyway
- //
- if ( this.stateWasChanged &&
- ( this.tlsContents.ReferenceCountValue > 1 ||
- !this.tlsContents.IsImpersonating ))
- {
- Win32Native.TOKEN_PRIVILEGE newState = new Win32Native.TOKEN_PRIVILEGE();
- newState.PrivilegeCount = 1;
- newState.Privilege.Luid = this.luid;
- newState.Privilege.Attributes = ( this.initialState ? Win32Native.SE_PRIVILEGE_ENABLED : Win32Native.SE_PRIVILEGE_DISABLED );
- Win32Native.TOKEN_PRIVILEGE previousState = new Win32Native.TOKEN_PRIVILEGE();
- uint previousSize = 0;
- if ( false == Win32Native.AdjustTokenPrivileges(
- this.tlsContents.ThreadHandle,
- false,
- ref newState,
- ( uint )Marshal.SizeOf( previousState ),
- ref previousState,
- ref previousSize ))
- {
- error = Marshal.GetLastWin32Error();
- success = false;
- }
- }
- }
- finally
- {
- if ( success )
- {
- this.Reset();
- }
- }
- }
- if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
- {
- throw new OutOfMemoryException();
- }
- else if ( error == Win32Native.ERROR_ACCESS_DENIED )
- {
- throw new UnauthorizedAccessException();
- }
- else if ( error != 0 )
- {
- Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "AdjustTokenPrivileges() failed with unrecognized error code {0}", error ));
- throw new InvalidOperationException();
- }
- }
- #if false
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
- public static void RunWithPrivilege( string privilege, bool enabled, PrivilegedHelper helper )
- {
- if ( helper == null )
- {
- throw new ArgumentNullException( "helper" );
- }
- Contract.EndContractBlock();
-
- Privilege p = new Privilege( privilege );
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- if (enabled)
- {
- p.Enable();
- }
- else
- {
- p.Disable();
- }
- helper();
- }
- finally
- {
- p.Revert();
- }
- }
- #endif
- [System.Security.SecurityCritical] // auto-generated
- [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
- [ResourceExposure(ResourceScope.None)]
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
- private void Reset()
- {
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- }
- finally
- {
- this.stateWasChanged = false;
- this.initialState = false;
- this.needToRevert = false;
- if ( this.tlsContents != null )
- {
- if ( 0 == this.tlsContents.DecrementReferenceCount())
- {
- this.tlsContents = null;
- Thread.SetData( tlsSlot, null );
- }
- }
- }
- }
- }
- }