mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
11 KiB
220 lines
11 KiB
// <copyright file="PrivilegeEnabler.cs" company="Nick Lowe">
|
|
// Copyright © Nick Lowe 2009
|
|
// </copyright>
|
|
// <author>Nick Lowe</author>
|
|
// <email>nick@int-r.net</email>
|
|
// <url>http://processprivileges.codeplex.com/</url>
|
|
|
|
namespace ProcessPrivileges
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Security.Permissions;
|
|
|
|
/// <summary>Enables privileges on a process in a safe way, ensuring that they are returned to their original state when an operation that requires a privilege completes.</summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// using System;
|
|
/// using System.Diagnostics;
|
|
/// using ProcessPrivileges;
|
|
///
|
|
/// internal static class PrivilegeEnablerExample
|
|
/// {
|
|
/// public static void Main()
|
|
/// {
|
|
/// Process process = Process.GetCurrentProcess();
|
|
///
|
|
/// using (new PrivilegeEnabler(process, Privilege.TakeOwnership))
|
|
/// {
|
|
/// // Privilege is enabled within the using block.
|
|
/// Console.WriteLine(
|
|
/// "{0} => {1}",
|
|
/// Privilege.TakeOwnership,
|
|
/// process.GetPrivilegeState(Privilege.TakeOwnership));
|
|
/// }
|
|
///
|
|
/// // Privilege is disabled outside the using block.
|
|
/// Console.WriteLine(
|
|
/// "{0} => {1}",
|
|
/// Privilege.TakeOwnership,
|
|
/// process.GetPrivilegeState(Privilege.TakeOwnership));
|
|
/// }
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
/// <remarks>
|
|
/// <para>When disabled, privileges are enabled until the instance of the PrivilegeEnabler class is disposed.</para>
|
|
/// <para>If the privilege specified is already enabled, it is not modified and will not be disabled when the instance of the PrivilegeEnabler class is disposed.</para>
|
|
/// <para>If desired, multiple privileges can be specified in the constructor.</para>
|
|
/// <para>If using multiple instances on the same process, do not dispose of them out-of-order. Making use of a using statement, the recommended method, enforces this.</para>
|
|
/// <para>For more information on privileges, see:</para>
|
|
/// <para><a href="http://msdn.microsoft.com/en-us/library/aa379306.aspx">Privileges</a></para>
|
|
/// <para><a href="http://msdn.microsoft.com/en-us/library/bb530716.aspx">Privilege Constants</a></para>
|
|
/// </remarks>
|
|
public sealed class PrivilegeEnabler : IDisposable
|
|
{
|
|
private static readonly Dictionary<Privilege, PrivilegeEnabler> sharedPrivileges =
|
|
new Dictionary<Privilege, PrivilegeEnabler>();
|
|
|
|
private static readonly Dictionary<Process, AccessTokenHandle> accessTokenHandles =
|
|
new Dictionary<Process, AccessTokenHandle>();
|
|
|
|
private AccessTokenHandle accessTokenHandle;
|
|
|
|
private bool disposed;
|
|
|
|
private bool ownsHandle;
|
|
|
|
private Process process;
|
|
|
|
/// <summary>Initializes a new instance of the PrivilegeEnabler class.</summary>
|
|
/// <param name="accessTokenHandle">The <see cref="AccessTokenHandle"/> for a <see cref="Process"/> on which privileges should be enabled.</param>
|
|
/// <exception cref="InvalidOperationException">Thrown when another instance exists and has not been disposed.</exception>
|
|
/// <permission cref="SecurityAction.LinkDemand">Requires the immediate caller to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
public PrivilegeEnabler(AccessTokenHandle accessTokenHandle)
|
|
{
|
|
this.accessTokenHandle = accessTokenHandle;
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the PrivilegeEnabler class.</summary>
|
|
/// <param name="process">The <see cref="Process"/> on which privileges should be enabled.</param>
|
|
/// <exception cref="InvalidOperationException">Thrown when another instance exists and has not been disposed.</exception>
|
|
/// <permission cref="SecurityAction.LinkDemand">Requires the immediate caller to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
public PrivilegeEnabler(Process process)
|
|
{
|
|
lock (accessTokenHandles)
|
|
{
|
|
if (accessTokenHandles.ContainsKey(process))
|
|
{
|
|
this.accessTokenHandle = accessTokenHandles[process];
|
|
}
|
|
else
|
|
{
|
|
this.accessTokenHandle =
|
|
process.GetAccessTokenHandle(TokenAccessRights.AdjustPrivileges | TokenAccessRights.Query);
|
|
accessTokenHandles.Add(process, this.accessTokenHandle);
|
|
this.ownsHandle = true;
|
|
}
|
|
}
|
|
|
|
this.process = process;
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the PrivilegeEnabler class with the specified privileges to be enabled.</summary>
|
|
/// <param name="accessTokenHandle">The <see cref="AccessTokenHandle"/> for a <see cref="Process"/> on which privileges should be enabled.</param>
|
|
/// <param name="privileges">The privileges to be enabled.</param>
|
|
/// <exception cref="Win32Exception">Thrown when an underlying Win32 function call does not succeed.</exception>
|
|
/// <permission cref="SecurityAction.LinkDemand">Requires the immediate caller to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
public PrivilegeEnabler(AccessTokenHandle accessTokenHandle, params Privilege[] privileges)
|
|
: this(accessTokenHandle)
|
|
{
|
|
foreach (Privilege privilege in privileges)
|
|
{
|
|
this.EnablePrivilege(privilege);
|
|
}
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the PrivilegeEnabler class with the specified privileges to be enabled.</summary>
|
|
/// <param name="process">The <see cref="Process"/> on which privileges should be enabled.</param>
|
|
/// <param name="privileges">The privileges to be enabled.</param>
|
|
/// <exception cref="Win32Exception">Thrown when an underlying Win32 function call does not succeed.</exception>
|
|
/// <permission cref="SecurityAction.LinkDemand">Requires the immediate caller to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
public PrivilegeEnabler(Process process, params Privilege[] privileges)
|
|
: this(process)
|
|
{
|
|
foreach (Privilege privilege in privileges)
|
|
{
|
|
this.EnablePrivilege(privilege);
|
|
}
|
|
}
|
|
|
|
/// <summary>Finalizes an instance of the PrivilegeEnabler class.</summary>
|
|
~PrivilegeEnabler()
|
|
{
|
|
this.InternalDispose();
|
|
}
|
|
|
|
/// <summary>Disposes of an instance of the PrivilegeEnabler class.</summary>
|
|
/// <exception cref="Win32Exception">Thrown when an underlying Win32 function call does not succeed.</exception>
|
|
/// <permission cref="SecurityAction.Demand">Requires the call stack to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
|
|
public void Dispose()
|
|
{
|
|
this.InternalDispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>Enables the specified <see cref="Privilege"/>.</summary>
|
|
/// <param name="privilege">The <see cref="Privilege"/> to be enabled.</param>
|
|
/// <returns>
|
|
/// <para>Result from the privilege adjustment.</para>
|
|
/// <para>If the <see cref="Privilege"/> is already enabled, <see cref="AdjustPrivilegeResult.None"/> is returned.</para>
|
|
/// <para>If the <see cref="Privilege"/> is owned by another instance of the PrivilegeEnabler class, <see cref="AdjustPrivilegeResult.None"/> is returned.</para>
|
|
/// <para>If a <see cref="Privilege"/> is removed from a process, it cannot be enabled.</para>
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// <para>When disabled, privileges are enabled until the instance of the PrivilegeEnabler class is disposed.</para>
|
|
/// <para>If the privilege specified is already enabled, it is not modified and will not be disabled when the instance of the PrivilegeEnabler class is disposed.</para>
|
|
/// </remarks>
|
|
/// <exception cref="Win32Exception">Thrown when an underlying Win32 function call does not succeed.</exception>
|
|
/// <permission cref="SecurityAction.LinkDemand">Requires the immediate caller to have FullTrust.</permission>
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
public AdjustPrivilegeResult EnablePrivilege(Privilege privilege)
|
|
{
|
|
lock (sharedPrivileges)
|
|
{
|
|
if (!sharedPrivileges.ContainsKey(privilege) &&
|
|
this.accessTokenHandle.GetPrivilegeState(privilege) == PrivilegeState.Disabled &&
|
|
this.accessTokenHandle.EnablePrivilege(privilege) == AdjustPrivilegeResult.PrivilegeModified)
|
|
{
|
|
sharedPrivileges.Add(privilege, this);
|
|
return AdjustPrivilegeResult.PrivilegeModified;
|
|
}
|
|
|
|
return AdjustPrivilegeResult.None;
|
|
}
|
|
}
|
|
|
|
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
|
|
private void InternalDispose()
|
|
{
|
|
if (!this.disposed)
|
|
{
|
|
lock (sharedPrivileges)
|
|
{
|
|
Privilege[] privileges = sharedPrivileges
|
|
.Where(keyValuePair => keyValuePair.Value == this)
|
|
.Select(keyValuePair => keyValuePair.Key)
|
|
.ToArray();
|
|
|
|
foreach (Privilege privilege in privileges)
|
|
{
|
|
this.accessTokenHandle.DisablePrivilege(privilege);
|
|
sharedPrivileges.Remove(privilege);
|
|
}
|
|
|
|
if (this.ownsHandle)
|
|
{
|
|
this.accessTokenHandle.Dispose();
|
|
lock (this.accessTokenHandle)
|
|
{
|
|
accessTokenHandles.Remove(this.process);
|
|
}
|
|
}
|
|
|
|
this.accessTokenHandle = null;
|
|
this.ownsHandle = false;
|
|
this.process = null;
|
|
|
|
this.disposed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|