Tuesday, March 25, 2014

NET (C#) Impersonation with Network Credentials

copyright from: platinumdogs.me

I required a C# class to enable ad-hoc user account impersonation for accessing resources both on the local machine and also on network machines, which I’ve reproduced here.
Of note, if you require impersonation in order to access network resources, you would intuitively select the logon type of LOGON32_LOGON_NETWORK, this however doesn’t work, as according to MSDN, this type of logon is used for fast authentication where the credentials are not added to the local credential cache.
If you require the impersonated logon to have network credentials, you must selectLOGON32_LOGON_NEW_CREDENTIALS as your logon type, which requires that you selectLOGON32_PROVIDER_WINNT50 as the logon provider type.
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
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
namespace Tools.Network
{
 public enum LogonType
 {
  LOGON32_LOGON_INTERACTIVE = 2,
  LOGON32_LOGON_NETWORK = 3,
  LOGON32_LOGON_BATCH = 4,
  LOGON32_LOGON_SERVICE = 5,
  LOGON32_LOGON_UNLOCK = 7,
  LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Win2K or higher
  LOGON32_LOGON_NEW_CREDENTIALS = 9 // Win2K or higher
 };
 
 public enum LogonProvider
 {
  LOGON32_PROVIDER_DEFAULT = 0,
  LOGON32_PROVIDER_WINNT35 = 1,
  LOGON32_PROVIDER_WINNT40 = 2,
  LOGON32_PROVIDER_WINNT50 = 3
 };
 
 public enum ImpersonationLevel
 {
  SecurityAnonymous = 0,
  SecurityIdentification = 1,
  SecurityImpersonation = 2,
  SecurityDelegation = 3
 }
 
 class Win32NativeMethods
 {
  [DllImport("advapi32.dll", SetLastError = true)]
  public static extern int LogonUser( string lpszUserName,
       string lpszDomain,
       string lpszPassword,
       int dwLogonType,
       int dwLogonProvider,
       ref IntPtr phToken);
 
  [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern int DuplicateToken( IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);
 
  [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern bool RevertToSelf();
 
  [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
  public static extern bool CloseHandle(IntPtr handle);
 }
 
 ///
 /// Allows code to be executed under the security context of a specified user account.
 ///
 ///  
 ///
 /// Implements IDispose, so can be used via a using-directive or method calls;
 ///  ...
 ///
 ///  var imp = new Impersonator( "myUsername", "myDomainname", "myPassword" );
 ///  imp.UndoImpersonation();
 ///
 ///  ...
 ///
 ///   var imp = new Impersonator();
 ///  imp.Impersonate("myUsername", "myDomainname", "myPassword");
 ///  imp.UndoImpersonation();
 ///
 ///  ...
 ///
 ///  using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
 ///  {
 ///   ...
 ///   1
 ///   ...
 ///  }
 ///
 ///  ...
 ///
 public class Impersonator : IDisposable
 {
  private WindowsImpersonationContext _wic;
 
  ///
  /// Begins impersonation with the given credentials, Logon type and Logon provider.
  ///
  ///
"userName">Name of the user.
  ///
"domainName">Name of the domain.
  ///
"password">The password. "System.String"/>
  ///
"logonType">Type of the logon.
  ///
"logonProvider">The logon provider. "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider"/>
  public Impersonator(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
  {
   Impersonate(userName, domainName, password, logonType, logonProvider);
  }
 
  ///
  /// Begins impersonation with the given credentials.
  ///
  ///
"userName">Name of the user.
  ///
"domainName">Name of the domain.
  ///
"password">The password. "System.String"/>
  public Impersonator(string userName, string domainName, string password)
  {
   Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
  }
 
  ///
  /// Initializes a new instance of the class.
  ///
  public Impersonator()
  {}
 
  ///
  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  ///
  public void Dispose()
  {
   UndoImpersonation();
  }
 
  ///
  /// Impersonates the specified user account.
  ///
  ///
"userName">Name of the user.
  ///
"domainName">Name of the domain.
  ///
"password">The password. "System.String"/>
  public void Impersonate(string userName, string domainName, string password)
  {
   Impersonate(userName, domainName, password, LogonType.LOGON32_LOGON_INTERACTIVE, LogonProvider.LOGON32_PROVIDER_DEFAULT);
  }
 
  ///
  /// Impersonates the specified user account.
  ///
  ///
"userName">Name of the user.
  ///
"domainName">Name of the domain.
  ///
"password">The password. "System.String"/>
  ///
"logonType">Type of the logon.
  ///
"logonProvider">The logon provider. "Mit.Sharepoint.WebParts.EventLogQuery.Network.LogonProvider"/>
  public void Impersonate(string userName, string domainName, string password, LogonType logonType, LogonProvider logonProvider)
  {
   UndoImpersonation();
 
   IntPtr logonToken = IntPtr.Zero;
   IntPtr logonTokenDuplicate = IntPtr.Zero;
   try
   {
    // revert to the application pool identity, saving the identity of the current requestor
    _wic = WindowsIdentity.Impersonate(IntPtr.Zero);
 
    // do logon & impersonate
    if (Win32NativeMethods.LogonUser(userName,
        domainName,
        password,
        (int)logonType,
        (int)logonProvider,
        ref logonToken) != 0)
    {
     if (Win32NativeMethods.DuplicateToken(logonToken, (int)ImpersonationLevel.SecurityImpersonation, ref logonTokenDuplicate) != 0)
     {
      var wi = new WindowsIdentity(logonTokenDuplicate);
      wi.Impersonate(); // discard the returned identity context (which is the context of the application pool)
     }
     else
      throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    else
     throw new Win32Exception(Marshal.GetLastWin32Error());
   }
   finally
   {
    if (logonToken != IntPtr.Zero)
     Win32NativeMethods.CloseHandle(logonToken);
 
    if (logonTokenDuplicate != IntPtr.Zero)
     Win32NativeMethods.CloseHandle(logonTokenDuplicate);
   }
  }
 
  ///
  /// Stops impersonation.
  ///
  private void UndoImpersonation()
  {
   // restore saved requestor identity
   if (_wic != null)
    _wic.Undo();
   _wic = null;
  }
 }
}

No comments: