| #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 |
| |
| #if NET_2_0 |
| // .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 static extern bool CloseHandle(IntPtr handle); |
| |
| [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] |
| private static extern 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 |
| #endif // NET_2_0 |
| |