123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- #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 || MONO_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
|