#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 { /// /// Impersonate a Windows Account /// /// /// /// This impersonates a Windows account. /// /// /// How the impersonation is done depends on the value of . /// 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. /// /// public class WindowsSecurityContext : SecurityContext, IOptionHandler { /// /// The impersonation modes for the /// /// /// /// See the property for /// details. /// /// public enum ImpersonationMode { /// /// Impersonate a user using the credentials supplied /// User, /// /// Revert this the thread to the credentials of the process /// 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 /// /// Default constructor /// /// /// /// Default constructor /// /// public WindowsSecurityContext() { } #endregion #region Public Properties /// /// Gets or sets the impersonation mode for this security context /// /// /// The impersonation mode for this security context /// /// /// /// Impersonate either a user with user credentials or /// revert this thread to the credentials of the process. /// The value is one of the /// enum. /// /// /// The default value is /// /// /// When the mode is set to /// the user's credentials are established using the /// , and /// values. /// /// /// When the mode is set to /// no other properties need to be set. If the calling thread is /// impersonating then it will be reverted back to the process credentials. /// /// public ImpersonationMode Credentials { get { return m_impersonationMode; } set { m_impersonationMode = value; } } /// /// Gets or sets the Windows username for this security context /// /// /// The Windows username for this security context /// /// /// /// This property must be set if /// is set to (the default setting). /// /// public string UserName { get { return m_userName; } set { m_userName = value; } } /// /// Gets or sets the Windows domain name for this security context /// /// /// The Windows domain name for this security context /// /// /// /// The default value for is the local machine name /// taken from the property. /// /// /// This property must be set if /// is set to (the default setting). /// /// public string DomainName { get { return m_domainName; } set { m_domainName = value; } } /// /// Sets the password for the Windows account specified by the and properties. /// /// /// The password for the Windows account specified by the and properties. /// /// /// /// This property must be set if /// is set to (the default setting). /// /// public string Password { set { m_password = value; } } #endregion #region IOptionHandler Members /// /// Initialize the SecurityContext based on the options set. /// /// /// /// This is part of the delayed object /// activation scheme. The method must /// be called on this object after the configuration properties have /// been set. Until is called this /// object is in an undefined state and must not be used. /// /// /// If any of the configuration properties are modified then /// must be called again. /// /// /// The security context will try to Logon the specified user account and /// capture a primary token for impersonation. /// /// /// The required , /// or properties were not specified. 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 /// /// Impersonate the Windows account specified by the and properties. /// /// caller provided state /// /// An instance that will revoke the impersonation of this SecurityContext /// /// /// /// Depending on the property either /// impersonate a user using credentials supplied or revert /// to the process credentials. /// /// 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; } /// /// Create a given the userName, domainName and password. /// /// the user name /// the domain name /// the password /// the for the account specified /// /// /// Uses the Windows API call LogonUser to get a principal token for the account. This /// token is used to initialize the WindowsIdentity. /// /// #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 /// /// Adds to /// /// /// /// Helper class to expose the /// through the interface. /// /// private sealed class DisposableImpersonationContext : IDisposable { private readonly WindowsImpersonationContext m_impersonationContext; /// /// Constructor /// /// the impersonation context being wrapped /// /// /// Constructor /// /// public DisposableImpersonationContext(WindowsImpersonationContext impersonationContext) { m_impersonationContext = impersonationContext; } /// /// Revert the impersonation /// /// /// /// Revert the impersonation /// /// public void Dispose() { m_impersonationContext.Undo(); } } #endregion } } #endif // !CLI_1_0 #endif // !SSCLI #endif // !MONO #endif // !NETCF