-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathPrivilegeEnabler.cs
More file actions
221 lines (200 loc) · 10.6 KB
/
PrivilegeEnabler.cs
File metadata and controls
221 lines (200 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// <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.ComponentModel;
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;
}
}
}
}
}