PageRenderTime 59ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/referencesource/mscorlib/system/security/accesscontrol/privilege.cs

https://github.com/pruiz/mono
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
  1. // ==++==
  2. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. // ==--==
  6. /*============================================================
  7. **
  8. ** Class: Privilege
  9. **
  10. ** Purpose: Managed wrapper for NT privileges.
  11. **
  12. ** Date: July 1, 2004
  13. **
  14. ===========================================================*/
  15. using Microsoft.Win32;
  16. using Microsoft.Win32.SafeHandles;
  17. using System.Collections;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System.Runtime.ConstrainedExecution;
  21. using System.Security.Permissions;
  22. using System.Security.Principal;
  23. using System.Threading;
  24. using System.Runtime.Versioning;
  25. using System.Diagnostics.Contracts;
  26. namespace System.Security.AccessControl
  27. {
  28. using CultureInfo = System.Globalization.CultureInfo;
  29. using FCall = System.Security.Principal.Win32;
  30. using Luid = Microsoft.Win32.Win32Native.LUID;
  31. #if false
  32. internal delegate void PrivilegedHelper();
  33. #endif
  34. internal sealed class Privilege
  35. {
  36. private static LocalDataStoreSlot tlsSlot = Thread.AllocateDataSlot();
  37. private static Hashtable privileges = new Hashtable();
  38. private static Hashtable luids = new Hashtable();
  39. private static ReaderWriterLock privilegeLock = new ReaderWriterLock();
  40. private bool needToRevert = false;
  41. private bool initialState = false;
  42. private bool stateWasChanged = false;
  43. [System.Security.SecurityCritical] // auto-generated
  44. private Luid luid;
  45. private readonly Thread currentThread = Thread.CurrentThread;
  46. private TlsContents tlsContents = null;
  47. public const string CreateToken = "SeCreateTokenPrivilege";
  48. public const string AssignPrimaryToken = "SeAssignPrimaryTokenPrivilege";
  49. public const string LockMemory = "SeLockMemoryPrivilege";
  50. public const string IncreaseQuota = "SeIncreaseQuotaPrivilege";
  51. public const string UnsolicitedInput = "SeUnsolicitedInputPrivilege";
  52. public const string MachineAccount = "SeMachineAccountPrivilege";
  53. public const string TrustedComputingBase = "SeTcbPrivilege";
  54. public const string Security = "SeSecurityPrivilege";
  55. public const string TakeOwnership = "SeTakeOwnershipPrivilege";
  56. public const string LoadDriver = "SeLoadDriverPrivilege";
  57. public const string SystemProfile = "SeSystemProfilePrivilege";
  58. public const string SystemTime = "SeSystemtimePrivilege";
  59. public const string ProfileSingleProcess = "SeProfileSingleProcessPrivilege";
  60. public const string IncreaseBasePriority = "SeIncreaseBasePriorityPrivilege";
  61. public const string CreatePageFile = "SeCreatePagefilePrivilege";
  62. public const string CreatePermanent = "SeCreatePermanentPrivilege";
  63. public const string Backup = "SeBackupPrivilege";
  64. public const string Restore = "SeRestorePrivilege";
  65. public const string Shutdown = "SeShutdownPrivilege";
  66. public const string Debug = "SeDebugPrivilege";
  67. public const string Audit = "SeAuditPrivilege";
  68. public const string SystemEnvironment = "SeSystemEnvironmentPrivilege";
  69. public const string ChangeNotify = "SeChangeNotifyPrivilege";
  70. public const string RemoteShutdown = "SeRemoteShutdownPrivilege";
  71. public const string Undock = "SeUndockPrivilege";
  72. public const string SyncAgent = "SeSyncAgentPrivilege";
  73. public const string EnableDelegation = "SeEnableDelegationPrivilege";
  74. public const string ManageVolume = "SeManageVolumePrivilege";
  75. public const string Impersonate = "SeImpersonatePrivilege";
  76. public const string CreateGlobal = "SeCreateGlobalPrivilege";
  77. public const string TrustedCredentialManagerAccess = "SeTrustedCredManAccessPrivilege";
  78. public const string ReserveProcessor = "SeReserveProcessorPrivilege";
  79. //
  80. // This routine is a wrapper around a hashtable containing mappings
  81. // of privilege names to LUIDs
  82. //
  83. [System.Security.SecurityCritical] // auto-generated
  84. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  85. private static Luid LuidFromPrivilege( string privilege )
  86. {
  87. Luid luid;
  88. luid.LowPart = 0;
  89. luid.HighPart = 0;
  90. //
  91. // Look up the privilege LUID inside the cache
  92. //
  93. RuntimeHelpers.PrepareConstrainedRegions();
  94. try
  95. {
  96. privilegeLock.AcquireReaderLock( -1 );
  97. if ( luids.Contains( privilege ))
  98. {
  99. luid = ( Luid )luids[ privilege ];
  100. privilegeLock.ReleaseReaderLock();
  101. }
  102. else
  103. {
  104. privilegeLock.ReleaseReaderLock();
  105. if ( false == Win32Native.LookupPrivilegeValue( null, privilege, ref luid ))
  106. {
  107. int error = Marshal.GetLastWin32Error();
  108. if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
  109. {
  110. throw new OutOfMemoryException();
  111. }
  112. else if ( error == Win32Native.ERROR_ACCESS_DENIED )
  113. {
  114. throw new UnauthorizedAccessException();
  115. }
  116. else if ( error == Win32Native.ERROR_NO_SUCH_PRIVILEGE )
  117. {
  118. throw new ArgumentException(
  119. Environment.GetResourceString( "Argument_InvalidPrivilegeName",
  120. privilege ));
  121. }
  122. else
  123. {
  124. Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "LookupPrivilegeValue() failed with unrecognized error code {0}", error ));
  125. throw new InvalidOperationException();
  126. }
  127. }
  128. privilegeLock.AcquireWriterLock( -1 );
  129. }
  130. }
  131. finally
  132. {
  133. if ( privilegeLock.IsReaderLockHeld )
  134. {
  135. privilegeLock.ReleaseReaderLock();
  136. }
  137. if ( privilegeLock.IsWriterLockHeld )
  138. {
  139. if ( !luids.Contains( privilege ))
  140. {
  141. luids[ privilege ] = luid;
  142. privileges[ luid ] = privilege;
  143. }
  144. privilegeLock.ReleaseWriterLock();
  145. }
  146. }
  147. return luid;
  148. }
  149. private sealed class TlsContents : IDisposable
  150. {
  151. private bool disposed = false;
  152. private int referenceCount = 1;
  153. [System.Security.SecurityCritical] // auto-generated
  154. private SafeAccessTokenHandle threadHandle = new SafeAccessTokenHandle( IntPtr.Zero );
  155. private bool isImpersonating = false;
  156. [System.Security.SecurityCritical] // auto-generated
  157. private static volatile SafeAccessTokenHandle processHandle = new SafeAccessTokenHandle( IntPtr.Zero );
  158. private static readonly object syncRoot = new object();
  159. #region Constructor and Finalizer
  160. [System.Security.SecuritySafeCritical] // auto-generated
  161. static TlsContents()
  162. {
  163. }
  164. [System.Security.SecurityCritical] // auto-generated
  165. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  166. [ResourceExposure(ResourceScope.None)]
  167. [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
  168. public TlsContents()
  169. {
  170. int error = 0;
  171. int cachingError = 0;
  172. bool success = true;
  173. if ( processHandle.IsInvalid)
  174. {
  175. lock( syncRoot )
  176. {
  177. if ( processHandle.IsInvalid)
  178. {
  179. SafeAccessTokenHandle localProcessHandle;
  180. if ( false == Win32Native.OpenProcessToken(
  181. Win32Native.GetCurrentProcess(),
  182. TokenAccessLevels.Duplicate,
  183. out localProcessHandle))
  184. {
  185. cachingError = Marshal.GetLastWin32Error();
  186. success = false;
  187. }
  188. processHandle = localProcessHandle;
  189. }
  190. }
  191. }
  192. RuntimeHelpers.PrepareConstrainedRegions();
  193. try
  194. {
  195. // Make the sequence non-interruptible
  196. }
  197. finally
  198. {
  199. try
  200. {
  201. //
  202. // Open the thread token; if there is no thread token, get one from
  203. // the process token by impersonating self.
  204. //
  205. SafeAccessTokenHandle threadHandleBefore = this.threadHandle;
  206. error = FCall.OpenThreadToken(
  207. TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
  208. WinSecurityContext.Process,
  209. out this.threadHandle );
  210. unchecked { error &= ~(int)0x80070000; }
  211. if ( error != 0 )
  212. {
  213. if ( success == true )
  214. {
  215. this.threadHandle = threadHandleBefore;
  216. if ( error != Win32Native.ERROR_NO_TOKEN )
  217. {
  218. success = false;
  219. }
  220. Contract.Assert( this.isImpersonating == false, "Incorrect isImpersonating state" );
  221. if ( success == true )
  222. {
  223. error = 0;
  224. if ( false == Win32Native.DuplicateTokenEx(
  225. processHandle,
  226. TokenAccessLevels.Impersonate | TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
  227. IntPtr.Zero,
  228. Win32Native.SECURITY_IMPERSONATION_LEVEL.Impersonation,
  229. System.Security.Principal.TokenType.TokenImpersonation,
  230. ref this.threadHandle ))
  231. {
  232. error = Marshal.GetLastWin32Error();
  233. success = false;
  234. }
  235. }
  236. if ( success == true )
  237. {
  238. error = FCall.SetThreadToken( this.threadHandle );
  239. unchecked { error &= ~(int)0x80070000; }
  240. if ( error != 0 )
  241. {
  242. success = false;
  243. }
  244. }
  245. if ( success == true )
  246. {
  247. this.isImpersonating = true;
  248. }
  249. }
  250. else
  251. {
  252. error = cachingError;
  253. }
  254. }
  255. else
  256. {
  257. success = true;
  258. }
  259. }
  260. finally
  261. {
  262. if ( !success )
  263. {
  264. Dispose();
  265. }
  266. }
  267. }
  268. if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
  269. {
  270. throw new OutOfMemoryException();
  271. }
  272. else if ( error == Win32Native.ERROR_ACCESS_DENIED ||
  273. error == Win32Native.ERROR_CANT_OPEN_ANONYMOUS )
  274. {
  275. throw new UnauthorizedAccessException();
  276. }
  277. else if ( error != 0 )
  278. {
  279. Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "WindowsIdentity.GetCurrentThreadToken() failed with unrecognized error code {0}", error ));
  280. throw new InvalidOperationException();
  281. }
  282. }
  283. [System.Security.SecuritySafeCritical]
  284. ~TlsContents()
  285. {
  286. if ( !this.disposed )
  287. {
  288. Dispose( false );
  289. }
  290. }
  291. #endregion
  292. #region IDisposable implementation
  293. [System.Security.SecuritySafeCritical] // overrides public transparent member
  294. public void Dispose()
  295. {
  296. Dispose( true );
  297. GC.SuppressFinalize( this );
  298. }
  299. [System.Security.SecurityCritical] // auto-generated
  300. private void Dispose( bool disposing )
  301. {
  302. if ( this.disposed ) return;
  303. if ( disposing )
  304. {
  305. if ( this.threadHandle != null )
  306. {
  307. this.threadHandle.Dispose();
  308. this.threadHandle = null;
  309. }
  310. }
  311. if ( this.isImpersonating )
  312. {
  313. FCall.RevertToSelf();
  314. }
  315. this.disposed = true;
  316. }
  317. #endregion
  318. #region Reference Counting
  319. public void IncrementReferenceCount()
  320. {
  321. this.referenceCount++;
  322. }
  323. [System.Security.SecurityCritical] // auto-generated
  324. public int DecrementReferenceCount()
  325. {
  326. int result = --this.referenceCount;
  327. if ( result == 0 )
  328. {
  329. Dispose();
  330. }
  331. return result;
  332. }
  333. public int ReferenceCountValue
  334. {
  335. get { return this.referenceCount; }
  336. }
  337. #endregion
  338. #region Properties
  339. public SafeAccessTokenHandle ThreadHandle
  340. {
  341. [System.Security.SecurityCritical] // auto-generated
  342. get { return this.threadHandle; }
  343. }
  344. public bool IsImpersonating
  345. {
  346. get { return this.isImpersonating; }
  347. }
  348. #endregion
  349. }
  350. #region Constructors
  351. [System.Security.SecurityCritical] // auto-generated
  352. public Privilege( string privilegeName )
  353. {
  354. if ( privilegeName == null )
  355. {
  356. throw new ArgumentNullException( "privilegeName" );
  357. }
  358. Contract.EndContractBlock();
  359. this.luid = LuidFromPrivilege( privilegeName );
  360. }
  361. #endregion
  362. //
  363. // Finalizer simply ensures that the privilege was not leaked
  364. //
  365. [System.Security.SecuritySafeCritical]
  366. ~Privilege()
  367. {
  368. Contract.Assert( !this.needToRevert, "Must revert privileges that you alter!" );
  369. if ( this.needToRevert )
  370. {
  371. Revert();
  372. }
  373. }
  374. #region Public interface
  375. [System.Security.SecurityCritical] // auto-generated
  376. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  377. public void Enable()
  378. {
  379. this.ToggleState( true );
  380. }
  381. public bool NeedToRevert
  382. {
  383. get { return this.needToRevert; }
  384. }
  385. #endregion
  386. // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )]
  387. [System.Security.SecurityCritical] // auto-generated
  388. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  389. [ResourceExposure(ResourceScope.None)]
  390. [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain | ResourceScope.Assembly)]
  391. private void ToggleState( bool enable )
  392. {
  393. int error = 0;
  394. //
  395. // All privilege operations must take place on the same thread
  396. //
  397. if ( !this.currentThread.Equals( Thread.CurrentThread ))
  398. {
  399. throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustBeSameThread" ));
  400. }
  401. //
  402. // This privilege was already altered and needs to be reverted before it can be altered again
  403. //
  404. if ( this.needToRevert )
  405. {
  406. throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustRevertPrivilege" ));
  407. }
  408. //
  409. // Need to make this block of code non-interruptible so that it would preserve
  410. // consistency of thread oken state even in the face of catastrophic exceptions
  411. //
  412. RuntimeHelpers.PrepareConstrainedRegions();
  413. try
  414. {
  415. //
  416. // The payload is entirely in the finally block
  417. // This is how we ensure that the code will not be
  418. // interrupted by catastrophic exceptions
  419. //
  420. }
  421. finally
  422. {
  423. try
  424. {
  425. //
  426. // Retrieve TLS state
  427. //
  428. this.tlsContents = Thread.GetData( tlsSlot ) as TlsContents;
  429. if ( this.tlsContents == null )
  430. {
  431. this.tlsContents = new TlsContents();
  432. Thread.SetData( tlsSlot, this.tlsContents );
  433. }
  434. else
  435. {
  436. this.tlsContents.IncrementReferenceCount();
  437. }
  438. Win32Native.TOKEN_PRIVILEGE newState = new Win32Native.TOKEN_PRIVILEGE();
  439. newState.PrivilegeCount = 1;
  440. newState.Privilege.Luid = this.luid;
  441. newState.Privilege.Attributes = enable ? Win32Native.SE_PRIVILEGE_ENABLED : Win32Native.SE_PRIVILEGE_DISABLED;
  442. Win32Native.TOKEN_PRIVILEGE previousState = new Win32Native.TOKEN_PRIVILEGE();
  443. uint previousSize = 0;
  444. //
  445. // Place the new privilege on the thread token and remember the previous state.
  446. //
  447. if ( false == Win32Native.AdjustTokenPrivileges(
  448. this.tlsContents.ThreadHandle,
  449. false,
  450. ref newState,
  451. ( uint )Marshal.SizeOf( previousState ),
  452. ref previousState,
  453. ref previousSize ))
  454. {
  455. error = Marshal.GetLastWin32Error();
  456. }
  457. else if ( Win32Native.ERROR_NOT_ALL_ASSIGNED == Marshal.GetLastWin32Error())
  458. {
  459. error = Win32Native.ERROR_NOT_ALL_ASSIGNED;
  460. }
  461. else
  462. {
  463. //
  464. // This is the initial state that revert will have to go back to
  465. //
  466. this.initialState = (( previousState.Privilege.Attributes & Win32Native.SE_PRIVILEGE_ENABLED ) != 0 );
  467. //
  468. // Remember whether state has changed at all
  469. //
  470. this.stateWasChanged = ( this.initialState != enable );
  471. //
  472. // If we had to impersonate, or if the privilege state changed we'll need to revert
  473. //
  474. this.needToRevert = this.tlsContents.IsImpersonating || this.stateWasChanged;
  475. }
  476. }
  477. finally
  478. {
  479. if ( !this.needToRevert )
  480. {
  481. this.Reset();
  482. }
  483. }
  484. }
  485. if ( error == Win32Native.ERROR_NOT_ALL_ASSIGNED )
  486. {
  487. throw new PrivilegeNotHeldException( privileges[this.luid] as string );
  488. }
  489. if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
  490. {
  491. throw new OutOfMemoryException();
  492. }
  493. else if ( error == Win32Native.ERROR_ACCESS_DENIED ||
  494. error == Win32Native.ERROR_CANT_OPEN_ANONYMOUS )
  495. {
  496. throw new UnauthorizedAccessException();
  497. }
  498. else if ( error != 0 )
  499. {
  500. Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "AdjustTokenPrivileges() failed with unrecognized error code {0}", error ));
  501. throw new InvalidOperationException();
  502. }
  503. }
  504. // [SecurityPermission( SecurityAction.Demand, TogglePrivileges=true )]
  505. [System.Security.SecurityCritical] // auto-generated
  506. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  507. public void Revert()
  508. {
  509. int error = 0;
  510. if ( !this.currentThread.Equals( Thread.CurrentThread ))
  511. {
  512. throw new InvalidOperationException( Environment.GetResourceString( "InvalidOperation_MustBeSameThread" ));
  513. }
  514. if ( !this.NeedToRevert )
  515. {
  516. return;
  517. }
  518. //
  519. // This code must be eagerly prepared and non-interruptible.
  520. //
  521. RuntimeHelpers.PrepareConstrainedRegions();
  522. try
  523. {
  524. //
  525. // The payload is entirely in the finally block
  526. // This is how we ensure that the code will not be
  527. // interrupted by catastrophic exceptions
  528. //
  529. }
  530. finally
  531. {
  532. bool success = true;
  533. try
  534. {
  535. //
  536. // Only call AdjustTokenPrivileges if we're not going to be reverting to self,
  537. // on this Revert, since doing the latter obliterates the thread token anyway
  538. //
  539. if ( this.stateWasChanged &&
  540. ( this.tlsContents.ReferenceCountValue > 1 ||
  541. !this.tlsContents.IsImpersonating ))
  542. {
  543. Win32Native.TOKEN_PRIVILEGE newState = new Win32Native.TOKEN_PRIVILEGE();
  544. newState.PrivilegeCount = 1;
  545. newState.Privilege.Luid = this.luid;
  546. newState.Privilege.Attributes = ( this.initialState ? Win32Native.SE_PRIVILEGE_ENABLED : Win32Native.SE_PRIVILEGE_DISABLED );
  547. Win32Native.TOKEN_PRIVILEGE previousState = new Win32Native.TOKEN_PRIVILEGE();
  548. uint previousSize = 0;
  549. if ( false == Win32Native.AdjustTokenPrivileges(
  550. this.tlsContents.ThreadHandle,
  551. false,
  552. ref newState,
  553. ( uint )Marshal.SizeOf( previousState ),
  554. ref previousState,
  555. ref previousSize ))
  556. {
  557. error = Marshal.GetLastWin32Error();
  558. success = false;
  559. }
  560. }
  561. }
  562. finally
  563. {
  564. if ( success )
  565. {
  566. this.Reset();
  567. }
  568. }
  569. }
  570. if ( error == Win32Native.ERROR_NOT_ENOUGH_MEMORY )
  571. {
  572. throw new OutOfMemoryException();
  573. }
  574. else if ( error == Win32Native.ERROR_ACCESS_DENIED )
  575. {
  576. throw new UnauthorizedAccessException();
  577. }
  578. else if ( error != 0 )
  579. {
  580. Contract.Assert( false, string.Format( CultureInfo.InvariantCulture, "AdjustTokenPrivileges() failed with unrecognized error code {0}", error ));
  581. throw new InvalidOperationException();
  582. }
  583. }
  584. #if false
  585. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
  586. public static void RunWithPrivilege( string privilege, bool enabled, PrivilegedHelper helper )
  587. {
  588. if ( helper == null )
  589. {
  590. throw new ArgumentNullException( "helper" );
  591. }
  592. Contract.EndContractBlock();
  593. Privilege p = new Privilege( privilege );
  594. RuntimeHelpers.PrepareConstrainedRegions();
  595. try
  596. {
  597. if (enabled)
  598. {
  599. p.Enable();
  600. }
  601. else
  602. {
  603. p.Disable();
  604. }
  605. helper();
  606. }
  607. finally
  608. {
  609. p.Revert();
  610. }
  611. }
  612. #endif
  613. [System.Security.SecurityCritical] // auto-generated
  614. [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
  615. [ResourceExposure(ResourceScope.None)]
  616. [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
  617. private void Reset()
  618. {
  619. RuntimeHelpers.PrepareConstrainedRegions();
  620. try
  621. {
  622. }
  623. finally
  624. {
  625. this.stateWasChanged = false;
  626. this.initialState = false;
  627. this.needToRevert = false;
  628. if ( this.tlsContents != null )
  629. {
  630. if ( 0 == this.tlsContents.DecrementReferenceCount())
  631. {
  632. this.tlsContents = null;
  633. Thread.SetData( tlsSlot, null );
  634. }
  635. }
  636. }
  637. }
  638. }
  639. }