jt
2021-06-10 5d0d028456874576560552f5a5c4e8b801786f11
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
 
// .NET Compact Framework 1.0 has no support for WindowsIdentity
#if !NETCF 
// MONO 1.0 has no support for Win32 Logon APIs
#if !MONO
// SSCLI 1.0 has no support for Win32 Logon APIs
#if !SSCLI
// We don't want framework or platform specific code in the CLI version of log4net
#if !CLI_1_0
 
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
 
using log4net.Core;
 
namespace log4net.Util
{
    /// <summary>
    /// Impersonate a Windows Account
    /// </summary>
    /// <remarks>
    /// <para>
    /// This <see cref="SecurityContext"/> impersonates a Windows account.
    /// </para>
    /// <para>
    /// How the impersonation is done depends on the value of <see cref="Impersonate"/>.
    /// This allows the context to either impersonate a set of user credentials specified 
    /// using username, domain name and password or to revert to the process credentials.
    /// </para>
    /// </remarks>
    public class WindowsSecurityContext : SecurityContext, IOptionHandler
    {
        /// <summary>
        /// The impersonation modes for the <see cref="WindowsSecurityContext"/>
        /// </summary>
        /// <remarks>
        /// <para>
        /// See the <see cref="WindowsSecurityContext.Credentials"/> property for
        /// details.
        /// </para>
        /// </remarks>
        public enum ImpersonationMode
        {
            /// <summary>
            /// Impersonate a user using the credentials supplied
            /// </summary>
            User,
 
            /// <summary>
            /// Revert this the thread to the credentials of the process
            /// </summary>
            Process
        }
 
        #region Member Variables
 
        private ImpersonationMode m_impersonationMode = ImpersonationMode.User;
        private string m_userName;
        private string m_domainName = Environment.MachineName;
        private string m_password;
        private WindowsIdentity m_identity;
 
        #endregion
 
        #region Constructor
 
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <remarks>
        /// <para>
        /// Default constructor
        /// </para>
        /// </remarks>
        public WindowsSecurityContext()
        {
        }
 
        #endregion
 
        #region Public Properties
 
        /// <summary>
        /// Gets or sets the impersonation mode for this security context
        /// </summary>
        /// <value>
        /// The impersonation mode for this security context
        /// </value>
        /// <remarks>
        /// <para>
        /// Impersonate either a user with user credentials or
        /// revert this thread to the credentials of the process.
        /// The value is one of the <see cref="ImpersonationMode"/>
        /// enum.
        /// </para>
        /// <para>
        /// The default value is <see cref="ImpersonationMode.User"/>
        /// </para>
        /// <para>
        /// When the mode is set to <see cref="ImpersonationMode.User"/>
        /// the user's credentials are established using the
        /// <see cref="UserName"/>, <see cref="DomainName"/> and <see cref="Password"/>
        /// values.
        /// </para>
        /// <para>
        /// When the mode is set to <see cref="ImpersonationMode.Process"/>
        /// no other properties need to be set. If the calling thread is 
        /// impersonating then it will be reverted back to the process credentials.
        /// </para>
        /// </remarks>
        public ImpersonationMode Credentials
        {
            get { return m_impersonationMode; }
            set { m_impersonationMode = value; }
        }
 
        /// <summary>
        /// Gets or sets the Windows username for this security context
        /// </summary>
        /// <value>
        /// The Windows username for this security context
        /// </value>
        /// <remarks>
        /// <para>
        /// This property must be set if <see cref="Credentials"/>
        /// is set to <see cref="ImpersonationMode.User"/> (the default setting).
        /// </para>
        /// </remarks>
        public string UserName
        {
            get { return m_userName; }
            set { m_userName = value; }
        }
 
        /// <summary>
        /// Gets or sets the Windows domain name for this security context
        /// </summary>
        /// <value>
        /// The Windows domain name for this security context
        /// </value>
        /// <remarks>
        /// <para>
        /// The default value for <see cref="DomainName"/> is the local machine name
        /// taken from the <see cref="Environment.MachineName"/> property.
        /// </para>
        /// <para>
        /// This property must be set if <see cref="Credentials"/>
        /// is set to <see cref="ImpersonationMode.User"/> (the default setting).
        /// </para>
        /// </remarks>
        public string DomainName
        {
            get { return m_domainName; }
            set { m_domainName = value; }
        }
 
        /// <summary>
        /// Sets the password for the Windows account specified by the <see cref="UserName"/> and <see cref="DomainName"/> properties.
        /// </summary>
        /// <value>
        /// The password for the Windows account specified by the <see cref="UserName"/> and <see cref="DomainName"/> properties.
        /// </value>
        /// <remarks>
        /// <para>
        /// This property must be set if <see cref="Credentials"/>
        /// is set to <see cref="ImpersonationMode.User"/> (the default setting).
        /// </para>
        /// </remarks>
        public string Password
        {
            set { m_password = value; }
        }
 
        #endregion
 
        #region IOptionHandler Members
 
        /// <summary>
        /// Initialize the SecurityContext based on the options set.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is part of the <see cref="IOptionHandler"/> delayed object
        /// activation scheme. The <see cref="ActivateOptions"/> method must 
        /// be called on this object after the configuration properties have
        /// been set. Until <see cref="ActivateOptions"/> is called this
        /// object is in an undefined state and must not be used. 
        /// </para>
        /// <para>
        /// If any of the configuration properties are modified then 
        /// <see cref="ActivateOptions"/> must be called again.
        /// </para>
        /// <para>
        /// The security context will try to Logon the specified user account and
        /// capture a primary token for impersonation.
        /// </para>
        /// </remarks>
        /// <exception cref="ArgumentNullException">The required <see cref="UserName" />, 
        /// <see cref="DomainName" /> or <see cref="Password" /> properties were not specified.</exception>
        public void ActivateOptions()
        {
            if (m_impersonationMode == ImpersonationMode.User)
            {
                if (m_userName == null) throw new ArgumentNullException("m_userName");
                if (m_domainName == null) throw new ArgumentNullException("m_domainName");
                if (m_password == null) throw new ArgumentNullException("m_password");
 
                m_identity = LogonUser(m_userName, m_domainName, m_password);
            }
        }
 
        #endregion
 
        /// <summary>
        /// Impersonate the Windows account specified by the <see cref="UserName"/> and <see cref="DomainName"/> properties.
        /// </summary>
        /// <param name="state">caller provided state</param>
        /// <returns>
        /// An <see cref="IDisposable"/> instance that will revoke the impersonation of this SecurityContext
        /// </returns>
        /// <remarks>
        /// <para>
        /// Depending on the <see cref="Credentials"/> property either
        /// impersonate a user using credentials supplied or revert 
        /// to the process credentials.
        /// </para>
        /// </remarks>
        public override IDisposable Impersonate(object state)
        {
            if (m_impersonationMode == ImpersonationMode.User)
            {
                if (m_identity != null)
                {
                    return new DisposableImpersonationContext(m_identity.Impersonate());
                }
            }
            else if (m_impersonationMode == ImpersonationMode.Process)
            {
                // Impersonate(0) will revert to the process credentials
                return new DisposableImpersonationContext(WindowsIdentity.Impersonate(IntPtr.Zero));
            }
            return null;
        }
 
        /// <summary>
        /// Create a <see cref="WindowsIdentity"/> given the userName, domainName and password.
        /// </summary>
        /// <param name="userName">the user name</param>
        /// <param name="domainName">the domain name</param>
        /// <param name="password">the password</param>
        /// <returns>the <see cref="WindowsIdentity"/> for the account specified</returns>
        /// <remarks>
        /// <para>
        /// Uses the Windows API call LogonUser to get a principal token for the account. This
        /// token is used to initialize the WindowsIdentity.
        /// </para>
        /// </remarks>
#if NET_4_0
        [System.Security.SecuritySafeCritical]
#endif
        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, UnmanagedCode = true)]
        private static WindowsIdentity LogonUser(string userName, string domainName, string password)
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;
 
            // Call LogonUser to obtain a handle to an access token.
            IntPtr tokenHandle = IntPtr.Zero;
            if(!LogonUser(userName, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle))
            {
                NativeError error = NativeError.GetLastError();
                throw new Exception("Failed to LogonUser ["+userName+"] in Domain ["+domainName+"]. Error: "+ error.ToString());
            }
 
            const int SecurityImpersonation = 2;
            IntPtr dupeTokenHandle = IntPtr.Zero;
            if(!DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle))
            {
                NativeError error = NativeError.GetLastError();
                if (tokenHandle != IntPtr.Zero)
                {
                    CloseHandle(tokenHandle);
                }
                throw new Exception("Failed to DuplicateToken after LogonUser. Error: " + error.ToString());
            }
 
            WindowsIdentity identity = new WindowsIdentity(dupeTokenHandle);
 
            // Free the tokens.
            if (dupeTokenHandle != IntPtr.Zero) 
            {
                CloseHandle(dupeTokenHandle);
            }
            if (tokenHandle != IntPtr.Zero)
            {
                CloseHandle(tokenHandle);
            }
 
            return identity;
        }
 
        #region Native Method Stubs
 
        [DllImport("advapi32.dll", SetLastError=true)]
        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
 
        [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
        private extern static bool CloseHandle(IntPtr handle);
 
        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
 
        #endregion
 
        #region DisposableImpersonationContext class
 
        /// <summary>
        /// Adds <see cref="IDisposable"/> to <see cref="WindowsImpersonationContext"/>
        /// </summary>
        /// <remarks>
        /// <para>
        /// Helper class to expose the <see cref="WindowsImpersonationContext"/>
        /// through the <see cref="IDisposable"/> interface.
        /// </para>
        /// </remarks>
        private sealed class DisposableImpersonationContext : IDisposable
        {
            private readonly WindowsImpersonationContext m_impersonationContext;
 
            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="impersonationContext">the impersonation context being wrapped</param>
            /// <remarks>
            /// <para>
            /// Constructor
            /// </para>
            /// </remarks>
            public DisposableImpersonationContext(WindowsImpersonationContext impersonationContext)
            {
                m_impersonationContext = impersonationContext;
            }
 
            /// <summary>
            /// Revert the impersonation
            /// </summary>
            /// <remarks>
            /// <para>
            /// Revert the impersonation
            /// </para>
            /// </remarks>
            public void Dispose()
            {
                m_impersonationContext.Undo();
            }
        }
 
        #endregion
    }
}
 
#endif // !CLI_1_0
#endif // !SSCLI
#endif // !MONO
#endif // !NETCF