//
// Copyright © Nick Lowe 2009
//
// Nick Lowe
// nick@int-r.net
// http://processprivileges.codeplex.com/
namespace ProcessPrivileges
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Permissions;
/// 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.
///
///
/// 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));
/// }
/// }
///
///
///
/// When disabled, privileges are enabled until the instance of the PrivilegeEnabler class is disposed.
/// 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.
/// If desired, multiple privileges can be specified in the constructor.
/// 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.
/// For more information on privileges, see:
/// Privileges
/// Privilege Constants
///
public sealed class PrivilegeEnabler : IDisposable
{
private static readonly Dictionary sharedPrivileges =
new Dictionary();
private static readonly Dictionary accessTokenHandles =
new Dictionary();
private AccessTokenHandle accessTokenHandle;
private bool disposed;
private bool ownsHandle;
private Process process;
/// Initializes a new instance of the PrivilegeEnabler class.
/// The for a on which privileges should be enabled.
/// Thrown when another instance exists and has not been disposed.
/// Requires the immediate caller to have FullTrust.
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public PrivilegeEnabler(AccessTokenHandle accessTokenHandle)
{
this.accessTokenHandle = accessTokenHandle;
}
/// Initializes a new instance of the PrivilegeEnabler class.
/// The on which privileges should be enabled.
/// Thrown when another instance exists and has not been disposed.
/// Requires the immediate caller to have FullTrust.
[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;
}
/// Initializes a new instance of the PrivilegeEnabler class with the specified privileges to be enabled.
/// The for a on which privileges should be enabled.
/// The privileges to be enabled.
/// Thrown when an underlying Win32 function call does not succeed.
/// Requires the immediate caller to have FullTrust.
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public PrivilegeEnabler(AccessTokenHandle accessTokenHandle, params Privilege[] privileges)
: this(accessTokenHandle)
{
foreach (Privilege privilege in privileges)
{
this.EnablePrivilege(privilege);
}
}
/// Initializes a new instance of the PrivilegeEnabler class with the specified privileges to be enabled.
/// The on which privileges should be enabled.
/// The privileges to be enabled.
/// Thrown when an underlying Win32 function call does not succeed.
/// Requires the immediate caller to have FullTrust.
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public PrivilegeEnabler(Process process, params Privilege[] privileges)
: this(process)
{
foreach (Privilege privilege in privileges)
{
this.EnablePrivilege(privilege);
}
}
/// Finalizes an instance of the PrivilegeEnabler class.
~PrivilegeEnabler()
{
this.InternalDispose();
}
/// Disposes of an instance of the PrivilegeEnabler class.
/// Thrown when an underlying Win32 function call does not succeed.
/// Requires the call stack to have FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Dispose()
{
this.InternalDispose();
GC.SuppressFinalize(this);
}
/// Enables the specified .
/// The to be enabled.
///
/// Result from the privilege adjustment.
/// If the is already enabled, is returned.
/// If the is owned by another instance of the PrivilegeEnabler class, is returned.
/// If a is removed from a process, it cannot be enabled.
///
///
/// When disabled, privileges are enabled until the instance of the PrivilegeEnabler class is disposed.
/// 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.
///
/// Thrown when an underlying Win32 function call does not succeed.
/// Requires the immediate caller to have FullTrust.
[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;
}
}
}
}
}