You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by ni...@apache.org on 2004/11/12 23:33:30 UTC

cvs commit: logging-log4net/src/Appender AdoNetAppender.cs EventLogAppender.cs FileAppender.cs NetSendAppender.cs RollingFileAppender.cs

nicko       2004/11/12 14:33:30

  Modified:    src/Appender AdoNetAppender.cs EventLogAppender.cs
                        FileAppender.cs NetSendAppender.cs
                        RollingFileAppender.cs
  Added:       src/Core SecurityContext.cs SecurityContextProvider.cs
               src/Util NullSecurityContext.cs WindowsSecurityContext.cs
  Log:
  Added inital SecurityContext classes
  
  Revision  Changes    Path
  1.1                  logging-log4net/src/Core/SecurityContext.cs
  
  Index: SecurityContext.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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
  
  using System;
  
  namespace log4net.Core
  {
  	/// <summary>
  	/// A SecurityContext used by log4net when interacting with protected resources
  	/// </summary>
  	/// <author>Nicko Cadell</author>
  	public abstract class SecurityContext
  	{
  		/// <summary>
  		/// Impersonate this SecurityContext
  		/// </summary>
  		/// <param name="state">State supplied by the caller</param>
  		/// <returns>An <see cref="IDisposable"/> instance that will
  		/// revoke the impersonation of this SecurityContext, or <c>null</c></returns>
  		public abstract IDisposable Impersonate(object state);
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Core/SecurityContextProvider.cs
  
  Index: SecurityContextProvider.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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
  
  using System;
  
  using log4net.Util;
  
  namespace log4net.Core
  {
  	/// <summary>
  	/// A SecurityContext used by log4net when interacting with protected resources
  	/// </summary>
  	/// <author>Nicko Cadell</author>
  	public class SecurityContextProvider
  	{
  		/// <summary>
  		/// The default provider
  		/// </summary>
  		private static SecurityContextProvider s_defaultProvider = null;
  
  		/// <summary>
  		/// Gets or sets the default SecurityContextProvider
  		/// </summary>
  		/// <value>
  		/// The default SecurityContextProvider
  		/// </value>
  		/// <remarks>
  		/// <para>
  		/// ........... HOW LOOKUP PROVIDER
  		/// </para>
  		/// </remarks>
  		public static SecurityContextProvider DefaultProvider
  		{
  			get 
  			{
  				if (s_defaultProvider == null)
  				{
  					lock(typeof(SecurityContextProvider))
  					{
  						if (s_defaultProvider == null)
  						{
  							// Lookup the default provider
  							s_defaultProvider = CreateDefaultProvider();
  						}
  					}
  				}
  				return s_defaultProvider;
  			}
  			set 
  			{
  				s_defaultProvider = value;
  			}
  		}
  
  		private static SecurityContextProvider CreateDefaultProvider()
  		{
  			return new SecurityContextProvider();
  		}
  
  		/// <summary>
  		/// Protected default constructor to allow subclassing
  		/// </summary>
  		protected SecurityContextProvider()
  		{
  		}
  
  		/// <summary>
  		/// Create a SecurityContext for a consumer
  		/// </summary>
  		/// <param name="consumer">The consumer requesting the SecurityContext</param>
  		/// <returns>An impersonation context</returns>
  		/// <remarks>
  		/// <para>
  		/// The default implementation is to return a <see cref="NullSecurityContext"/>.
  		/// </para>
  		/// </remarks>
  		public virtual SecurityContext CreateSecurityContext(object consumer)
  		{
  			return NullSecurityContext.Instance;
  		}
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Util/NullSecurityContext.cs
  
  Index: NullSecurityContext.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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
  
  using System;
  
  using log4net.Core;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// A SecurityContext used when a SecurityContext is not required
  	/// </summary>
  	/// <author>Nicko Cadell</author>
  	public sealed class NullSecurityContext : SecurityContext
  	{
  		/// <summary>
  		/// Singleton instance of NullSecurityContext
  		/// </summary>
  		public static readonly NullSecurityContext Instance = new NullSecurityContext();
  
  		/// <summary>
  		/// Private constructor
  		/// </summary>
  		private NullSecurityContext()
  		{
  		}
  
  		/// <summary>
  		/// Impersonate this SecurityContext
  		/// </summary>
  		/// <param name="state">State supplied by the caller</param>
  		/// <returns><c>null</c></returns>
  		public override IDisposable Impersonate(object state)
  		{
  			return null;
  		}
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Util/WindowsSecurityContext.cs
  
  Index: WindowsSecurityContext.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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 Core version of log4net
  #if !CORE
  
  using System;
  using System.Runtime.InteropServices;
  using System.Security.Principal;
  using System.Security.Permissions;
  
  using log4net.Core;
  
  /*
   * Custom Logging Classes to support additional logging levels.
   */
  namespace log4net.Util
  {
  	/// <summary>
  	/// Impersonate a Windows Account
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// This <see cref="SecurityContext"/> impersonates a Windows account.
  	/// The account is specified using username, domain name and password.
  	/// </para>
  	/// </remarks>
  	public class WindowsSecurityContext : SecurityContext, IOptionHandler
  	{
  		#region Member Variables
  
  		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>
  		public WindowsSecurityContext()
  		{
  		}
  
  		#endregion
  
  		#region Public Properties
  
  		/// <summary>
  		/// The Windows username for this security context
  		/// </summary>
  		public string UserName
  		{
  			get { return m_userName; }
  			set { m_userName = value; }
  		}
  
  		/// <summary>
  		/// The Windows domain name for this security context
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// The default value for <see cref="DomainName"/> is the local machine name
  		/// taken from the <see cref="Environment.MachineName"/> property.
  		/// </para>
  		/// </remarks>
  		public string DomainName
  		{
  			get { return m_domainName; }
  			set { m_domainName = value; }
  		}
  
  		/// <summary>
  		/// The password for the Windows account specified by the <see cref="UserName"/> and <see cref="DomainName"/> properties.
  		/// </summary>
  		public string Password
  		{
  			get { return m_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_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>
  		public override IDisposable Impersonate(object state)
  		{
  			if (m_identity != null)
  			{
  				return new DisposableImpersonationContext(m_identity.Impersonate());
  			}
  			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 initialise the WindowsIdentity.
  		/// </para>
  		/// </remarks>
  		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>
  		/// Helper class to expose the <see cref="WindowsImpersonationContext"/>
  		/// through the <see cref="IDisposable"/> interface.
  		/// </summary>
  		private sealed class DisposableImpersonationContext : IDisposable
  		{
  			private readonly WindowsImpersonationContext m_impersonationContext;
  
  			public DisposableImpersonationContext(WindowsImpersonationContext impersonationContext)
  			{
  				m_impersonationContext = impersonationContext;
  			}
  
  			public void Dispose()
  			{
  				m_impersonationContext.Undo();
  			}
  		}
  
  		#endregion
  
  	}
  }
  
  #endif // !CORE
  #endif // !SSCLI
  #endif // !MONO
  #endif // !NETCF
  
  
  
  
  1.7       +36 -2     logging-log4net/src/Appender/AdoNetAppender.cs
  
  Index: AdoNetAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/AdoNetAppender.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- AdoNetAppender.cs	7 Jun 2004 01:09:38 -0000	1.6
  +++ AdoNetAppender.cs	12 Nov 2004 22:33:30 -0000	1.7
  @@ -292,6 +292,26 @@
   			set { m_useTransactions = value; }
   		}
   
  +		/// <summary>
  +		/// Gets or sets the <see cref="SecurityContext"/> used to call the NetSend method.
  +		/// </summary>
  +		/// <value>
  +		/// The <see cref="SecurityContext"/> used to call the NetSend method.
  +		/// </value>
  +		/// <remarks>
  +		/// <para>
  +		/// Unless a <see cref="SecurityContext"/> specified here for this appender
  +		/// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
  +		/// security context to use. The default behaviour is to use the security context
  +		/// of the current thread.
  +		/// </para>
  +		/// </remarks>
  +		public SecurityContext SecurityContext 
  +		{
  +			get { return m_securityContext; }
  +			set { m_securityContext = value; }
  +		}
  +
   		#endregion // Public Instance Properties
   
   		#region Protected Instance Properties
  @@ -338,6 +358,12 @@
   		override public void ActivateOptions() 
   		{
   			base.ActivateOptions();
  +
  +			if (m_securityContext == null)
  +			{
  +				m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  +			}
  +
   			InitializeDatabaseConnection();
   
   			// Are we using a command object
  @@ -565,8 +591,11 @@
   				// Set the connection string
   				m_dbConnection.ConnectionString = m_connectionString;
   
  -				// Open the database connection
  -				m_dbConnection.Open();
  +				using(SecurityContext.Impersonate(this))
  +				{
  +					// Open the database connection
  +					m_dbConnection.Open();
  +				}
   			}
   			catch (System.Exception e)
   			{
  @@ -694,6 +723,11 @@
   		#endregion // Protected Instance Methods
   
   		#region Private Instance Fields
  +
  +		/// <summary>
  +		/// The security context to use for privileged calls
  +		/// </summary>
  +		private SecurityContext m_securityContext;
   
   		/// <summary>
   		/// The <see cref="IDbConnection" /> that will be used
  
  
  
  1.11      +76 -16    logging-log4net/src/Appender/EventLogAppender.cs
  
  Index: EventLogAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/EventLogAppender.cs,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- EventLogAppender.cs	10 Sep 2004 20:58:38 -0000	1.10
  +++ EventLogAppender.cs	12 Nov 2004 22:33:30 -0000	1.11
  @@ -44,7 +44,7 @@
   	/// <remarks>
   	/// <para>
   	/// The <c>EventID</c> of the event log entry can be
  -	/// set using the <c>EventLogEventID</c> property (<see cref="LoggingEvent.EventProperties"/>)
  +	/// set using the <c>EventLogEventID</c> property (<see cref="LoggingEvent.Properties"/>)
   	/// on the <see cref="LoggingEvent"/>.
   	/// </para>
   	/// <para>
  @@ -176,6 +176,26 @@
   			m_levelMapping.Add(mapping);
   		}
   
  +		/// <summary>
  +		/// Gets or sets the <see cref="SecurityContext"/> used to write to the EventLog.
  +		/// </summary>
  +		/// <value>
  +		/// The <see cref="SecurityContext"/> used to write to the EventLog.
  +		/// </value>
  +		/// <remarks>
  +		/// <para>
  +		/// Unless a <see cref="SecurityContext"/> specified here for this appender
  +		/// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
  +		/// security context to use. The default behaviour is to use the security context
  +		/// of the current thread.
  +		/// </para>
  +		/// </remarks>
  +		public SecurityContext SecurityContext 
  +		{
  +			get { return m_securityContext; }
  +			set { m_securityContext = value; }
  +		}
  +
   		#endregion // Public Instance Properties
   
   		#region Implementation of IOptionHandler
  @@ -199,29 +219,59 @@
   		override public void ActivateOptions() 
   		{
   			base.ActivateOptions();
  -			if (EventLog.SourceExists(m_applicationName))
  +
  +			if (m_securityContext == null)
  +			{
  +				m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  +			}
  +
  +			bool sourceAlreadyExists = false;
  +			string currentLogName = null;
  +
  +			using(SecurityContext.Impersonate(this))
   			{
  -				//
  -				// Re-register this to the current application if the user has changed
  -				// the application / logfile association
  -				//
  -				string logName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  -				if (logName != m_logName)
  +				sourceAlreadyExists = EventLog.SourceExists(m_applicationName);
  +				if (sourceAlreadyExists)
   				{
  -					LogLog.Debug("EventLogAppender: Changing event source [" + m_applicationName + "] from log [" + logName + "] to log [" + m_logName + "]");
  -					EventLog.DeleteEventSource(m_applicationName, m_machineName);
  -					EventLog.CreateEventSource(m_applicationName, m_logName, m_machineName);
  +					currentLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
   				}
   			}
  -			else
  +
  +			if (sourceAlreadyExists && currentLogName != m_logName)
  +			{
  +				LogLog.Debug("EventLogAppender: Changing event source [" + m_applicationName + "] from log [" + currentLogName + "] to log [" + m_logName + "]");
  +			}
  +			else if (!sourceAlreadyExists)
   			{
   				LogLog.Debug("EventLogAppender: Creating event source Source [" + m_applicationName + "] in log " + m_logName + "]");
  -				EventLog.CreateEventSource(m_applicationName, m_logName, m_machineName);
  +			}
  +
  +			string registeredLogName = null;
  +
  +			using(SecurityContext.Impersonate(this))
  +			{
  +				if (sourceAlreadyExists && currentLogName != m_logName)
  +				{
  +					//
  +					// Re-register this to the current application if the user has changed
  +					// the application / logfile association
  +					//
  +					EventLog.DeleteEventSource(m_applicationName, m_machineName);
  +					EventLog.CreateEventSource(m_applicationName, m_logName, m_machineName);
  +
  +					registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  +				}
  +				else if (!sourceAlreadyExists)
  +				{
  +					EventLog.CreateEventSource(m_applicationName, m_logName, m_machineName);
  +
  +					registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
  +				}
   			}
   
   			m_levelMapping.ActivateOptions();
   
  -			LogLog.Debug("EventLogAppender: Source [" + m_applicationName + "] is registered to log [" + EventLog.LogNameFromSourceName(m_applicationName, m_machineName) + "]");		
  +			LogLog.Debug("EventLogAppender: Source [" + m_applicationName + "] is registered to log [" + registeredLogName + "]");		
   		}
   
   		#endregion // Implementation of IOptionHandler
  @@ -237,7 +287,7 @@
   		/// <para>Writes the event to the system event log using the 
   		/// <see cref="ApplicationName"/>.</para>
   		/// 
  -		/// <para>If the event has an <c>EventID</c> property (see <see cref="LoggingEvent.EventProperties"/>)
  +		/// <para>If the event has an <c>EventID</c> property (see <see cref="LoggingEvent.Properties"/>)
   		/// set then this integer will be used as the event log event id.</para>
   		/// 
   		/// <para>
  @@ -284,7 +334,12 @@
   					eventTxt = eventTxt.Substring(0, 32000);
   				}
   
  -				EventLog.WriteEntry(m_applicationName, eventTxt, GetEntryType(loggingEvent.Level), eventID);
  +				EventLogEntryType entryType = GetEntryType(loggingEvent.Level);
  +
  +				using(SecurityContext.Impersonate(this))
  +				{
  +					EventLog.WriteEntry(m_applicationName, eventTxt, entryType, eventID);
  +				}
   			}
   			catch(Exception ex)
   			{
  @@ -366,6 +421,11 @@
   		/// Mapping from level object to EventLogEntryType
   		/// </summary>
   		private LevelMapping m_levelMapping = new LevelMapping();
  +
  +		/// <summary>
  +		/// The security context to use for privileged calls
  +		/// </summary>
  +		private SecurityContext m_securityContext;
   
   		#endregion // Private Instance Fields
   
  
  
  
  1.7       +55 -10    logging-log4net/src/Appender/FileAppender.cs
  
  Index: FileAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/FileAppender.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- FileAppender.cs	18 Sep 2004 16:34:16 -0000	1.6
  +++ FileAppender.cs	12 Nov 2004 22:33:30 -0000	1.7
  @@ -114,7 +114,7 @@
   		virtual public string File
   		{
   			get { return m_fileName; }
  -			set { m_fileName = ConvertToFullPath(value.Trim()); }
  +			set { m_fileName = value; }
   		}
   
   		/// <summary>
  @@ -155,6 +155,26 @@
   			set { m_encoding = value; }
   		}
   
  +		/// <summary>
  +		/// Gets or sets the <see cref="SecurityContext"/> used to write to the file.
  +		/// </summary>
  +		/// <value>
  +		/// The <see cref="SecurityContext"/> used to write to the file.
  +		/// </value>
  +		/// <remarks>
  +		/// <para>
  +		/// Unless a <see cref="SecurityContext"/> specified here for this appender
  +		/// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
  +		/// security context to use. The default behaviour is to use the security context
  +		/// of the current thread.
  +		/// </para>
  +		/// </remarks>
  +		public SecurityContext SecurityContext 
  +		{
  +			get { return m_securityContext; }
  +			set { m_securityContext = value; }
  +		}
  +
   		#endregion Public Instance Properties
   
   		#region Override implementation of AppenderSkeleton
  @@ -181,6 +201,17 @@
   		override public void ActivateOptions() 
   		{	
   			base.ActivateOptions();
  +
  +			if (m_securityContext == null)
  +			{
  +				m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  +			}
  +
  +			using(SecurityContext.Impersonate(this))
  +			{
  +				m_fileName = ConvertToFullPath(m_fileName.Trim());
  +			}
  +
   			if (m_fileName != null) 
   			{
   				SafeOpenFile(m_fileName, m_appendToFile);
  @@ -284,18 +315,28 @@
   				// Save these for later, allowing retries if file open fails
   				m_fileName = fileName;
   				m_appendToFile = append;
  +				FileStream fileStream = null;
   
  -				// Ensure that the directory structure exists
  -				string directoryFullName = Path.GetDirectoryName(fileName);
  -
  -				// Only create the directory if it does not exist
  -				// doing this check here resolves some permissions failures
  -				if (!Directory.Exists(directoryFullName))
  +				using(SecurityContext.Impersonate(this))
   				{
  -					Directory.CreateDirectory(directoryFullName);
  +					// Ensure that the directory structure exists
  +					string directoryFullName = Path.GetDirectoryName(fileName);
  +
  +					// Only create the directory if it does not exist
  +					// doing this check here resolves some permissions failures
  +					if (!Directory.Exists(directoryFullName))
  +					{
  +						Directory.CreateDirectory(directoryFullName);
  +					}
  +
  +					FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create;
  +					fileStream = new FileStream(fileName, fileOpenMode, FileAccess.Write, FileShare.Read);
   				}
   
  -				SetQWForFiles(new StreamWriter(fileName, append, m_encoding));
  +				if (fileStream != null)
  +				{
  +					SetQWForFiles(new StreamWriter(fileStream, m_encoding));
  +				}
   
   				WriteHeader();
   			}
  @@ -313,7 +354,6 @@
   			QuietWriter = new QuietTextWriter(writer, ErrorHandler);
   		}
   
  -
   		#endregion Protected Instance Methods
   
   		#region Protected Static Methods
  @@ -366,6 +406,11 @@
   		/// The encoding to use for the file stream.
   		/// </summary>
   		private Encoding m_encoding = Encoding.Default;
  +
  +		/// <summary>
  +		/// The security context to use for privileged calls
  +		/// </summary>
  +		private SecurityContext m_securityContext;
   
   		#endregion Private Instance Fields
   	}
  
  
  
  1.7       +46 -7     logging-log4net/src/Appender/NetSendAppender.cs
  
  Index: NetSendAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/NetSendAppender.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- NetSendAppender.cs	24 Jul 2004 14:54:02 -0000	1.6
  +++ NetSendAppender.cs	12 Nov 2004 22:33:30 -0000	1.7
  @@ -161,6 +161,11 @@
   		/// </summary>
   		private string m_recipient;
   
  +		/// <summary>
  +		/// The security context to use for privileged calls
  +		/// </summary>
  +		private SecurityContext m_securityContext;
  +
   		#endregion
   
   		#region Constructors
  @@ -229,6 +234,26 @@
   			set { m_server = value; }
   		}
   
  +		/// <summary>
  +		/// Gets or sets the <see cref="SecurityContext"/> used to call the NetSend method.
  +		/// </summary>
  +		/// <value>
  +		/// The <see cref="SecurityContext"/> used to call the NetSend method.
  +		/// </value>
  +		/// <remarks>
  +		/// <para>
  +		/// Unless a <see cref="SecurityContext"/> specified here for this appender
  +		/// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
  +		/// security context to use. The default behaviour is to use the security context
  +		/// of the current thread.
  +		/// </para>
  +		/// </remarks>
  +		public SecurityContext SecurityContext 
  +		{
  +			get { return m_securityContext; }
  +			set { m_securityContext = value; }
  +		}
  +
   		#endregion
   
   		#region Implementation of IOptionHandler
  @@ -261,6 +286,11 @@
   			{
   				throw new ArgumentNullException("Recipient", "The required property 'Recipient' was not specified.");
   			}
  +
  +			if (m_securityContext == null)
  +			{
  +				m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  +			}
   		}
   
   		#endregion
  @@ -278,17 +308,26 @@
   		/// </remarks>
   		protected override void Append(LoggingEvent loggingEvent) 
   		{
  -			string renderedLoggingEvent = RenderLoggingEvent(loggingEvent);
  +			NativeError nativeError = null;
   
  -			// Send the message
  -			int returnValue = NetMessageBufferSend(this.Server, this.Recipient, this.Sender, renderedLoggingEvent, renderedLoggingEvent.Length * Marshal.SystemDefaultCharSize);   
  +			// Render the event in the callers security context
  +			string renderedLoggingEvent = RenderLoggingEvent(loggingEvent);
   
  -			// Log the error if the message could not be sent
  -			if (returnValue != 0) 
  +			using(m_securityContext.Impersonate(this))
   			{
  -				// Lookup the native error
  -				NativeError nativeError = NativeError.GetError(returnValue);
  +				// Send the message
  +				int returnValue = NetMessageBufferSend(this.Server, this.Recipient, this.Sender, renderedLoggingEvent, renderedLoggingEvent.Length * Marshal.SystemDefaultCharSize);   
   
  +				// Log the error if the message could not be sent
  +				if (returnValue != 0) 
  +				{
  +					// Lookup the native error
  +					nativeError = NativeError.GetError(returnValue);
  +				}
  +			}
  +
  +			if (nativeError != null)
  +			{
   				// Handle the error over to the ErrorHandler
   				ErrorHandler.Error(nativeError.ToString() + " (Params: Server=" + this.Server + ", Recipient=" + this.Recipient + ", Sender=" + this.Sender + ")");
   			}
  
  
  
  1.10      +67 -23    logging-log4net/src/Appender/RollingFileAppender.cs
  
  Index: RollingFileAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/RollingFileAppender.cs,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- RollingFileAppender.cs	26 Sep 2004 22:41:08 -0000	1.9
  +++ RollingFileAppender.cs	12 Nov 2004 22:33:30 -0000	1.10
  @@ -510,9 +510,12 @@
   				long currentCount = 0;
   				if (append) 
   				{
  -					if (System.IO.File.Exists(fileName))
  +					using(SecurityContext.Impersonate(this))
   					{
  -						currentCount = (new FileInfo(fileName)).Length;
  +						if (System.IO.File.Exists(fileName))
  +						{
  +							currentCount = (new FileInfo(fileName)).Length;
  +						}
   					}
   				}
   
  @@ -545,8 +548,14 @@
   				sName = m_scheduledFilename;
   			}
   
  -			string fullPath = System.IO.Path.GetFullPath(sName);
  -			string fileName = System.IO.Path.GetFileName(fullPath);
  +			string fullPath = null;
  +			string fileName = null;
  +
  +			using(SecurityContext.Impersonate(this))
  +			{
  +				fullPath = System.IO.Path.GetFullPath(sName);
  +				fileName = System.IO.Path.GetFileName(fullPath);
  +			}
   
   			ArrayList arrayFiles = GetExistingFiles(fullPath);
   			InitializeRollBackups(fileName, arrayFiles);
  @@ -571,31 +580,35 @@
   		/// </summary>
   		/// <param name="baseFilePath"></param>
   		/// <returns></returns>
  -		private static ArrayList GetExistingFiles(string baseFilePath)
  +		private ArrayList GetExistingFiles(string baseFilePath)
   		{
   			ArrayList alFiles = new ArrayList();
   
  -			string directory = Path.GetDirectoryName(baseFilePath);
  -			LogLog.Debug("RollingFileAppender: Searching for existing files in ["+directory+"]");
  +			string directory = null;
   
  -			if (Directory.Exists(directory))
  +			using(SecurityContext.Impersonate(this))
   			{
  -				string baseFileName = Path.GetFileName(baseFilePath);
  +				directory = Path.GetDirectoryName(baseFilePath);
  +				if (Directory.Exists(directory))
  +				{
  +					string baseFileName = Path.GetFileName(baseFilePath);
   
  -				string[] files = Directory.GetFiles(directory, GetWildcardPatternForFile(baseFileName));
  +					string[] files = Directory.GetFiles(directory, GetWildcardPatternForFile(baseFileName));
   	
  -				if (files != null)
  -				{
  -					for (int i = 0; i < files.Length; i++) 
  +					if (files != null)
   					{
  -						string curFileName = Path.GetFileName(files[i]);
  -						if (curFileName.StartsWith(baseFileName))
  +						for (int i = 0; i < files.Length; i++) 
   						{
  -							alFiles.Add(curFileName);
  +							string curFileName = Path.GetFileName(files[i]);
  +							if (curFileName.StartsWith(baseFileName))
  +							{
  +								alFiles.Add(curFileName);
  +							}
   						}
   					}
   				}
   			}
  +			LogLog.Debug("RollingFileAppender: Searched for existing files in ["+directory+"]");
   			return alFiles;
   		}
   
  @@ -606,9 +619,13 @@
   		{
   			if (m_staticLogFileName && m_rollDate) 
   			{
  -				if (System.IO.File.Exists(m_baseFileName)) 
  +				if (FileExists(m_baseFileName)) 
   				{
  -					DateTime last = System.IO.File.GetLastWriteTime(m_baseFileName);
  +					DateTime last;
  +					using(SecurityContext.Impersonate(this))
  +					{
  +						last = System.IO.File.GetLastWriteTime(m_baseFileName);
  +					}
   					LogLog.Debug("RollingFileAppender: ["+last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"] vs. ["+m_now.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]");
   
   					if (!(last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo).Equals(m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo)))) 
  @@ -843,6 +860,11 @@
   				m_scheduledFilename = File + m_now.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);
   			}
   
  +			if (SecurityContext == null)
  +			{
  +				SecurityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
  +			}
  +
   			ExistingInit();
   	
   			base.ActivateOptions();
  @@ -910,7 +932,7 @@
   		/// <param name="toFile">New name for file.</param>
   		protected void RollFile(string fromFile, string toFile) 
   		{
  -			if (System.IO.File.Exists(fromFile))
  +			if (FileExists(fromFile))
   			{
   				// Delete the toFile if it exists
   				DeleteFile(toFile);
  @@ -919,7 +941,10 @@
   				try
   				{
   					LogLog.Debug("RollingFileAppender: Moving [" + fromFile + "] -> [" + toFile + "]");
  -					System.IO.File.Move(fromFile, toFile);
  +					using(SecurityContext.Impersonate(this))
  +					{
  +						System.IO.File.Move(fromFile, toFile);
  +					}
   				}
   				catch(Exception moveEx)
   				{
  @@ -931,6 +956,19 @@
   				LogLog.Warn("RollingFileAppender: Cannot RollFile [" + fromFile + "] -> [" + toFile + "]. Source does not exist");
   			}
   		}
  +
  +		/// <summary>
  +		/// Test if a file exists at a specified path
  +		/// </summary>
  +		/// <param name="path">the path to the file</param>
  +		/// <returns>true if the file exists</returns>
  +		protected bool FileExists(string path)
  +		{
  +			using(SecurityContext.Impersonate(this))
  +			{
  +				return System.IO.File.Exists(path);
  +			}
  +		}
     
   		/// <summary>
   		/// Deletes the specified file if it exists.
  @@ -938,7 +976,7 @@
   		/// <param name="fileName">The file to delete.</param>
   		protected void DeleteFile(string fileName) 
   		{
  -			if (System.IO.File.Exists(fileName)) 
  +			if (FileExists(fileName)) 
   			{
   				// We may not have permission to delete the file, or the file may be locked
   
  @@ -949,7 +987,10 @@
   				string tempFileName = fileName + "." + Environment.TickCount + ".DeletePending";
   				try
   				{
  -					System.IO.File.Move(fileName, tempFileName);
  +					using(SecurityContext.Impersonate(this))
  +					{
  +						System.IO.File.Move(fileName, tempFileName);
  +					}
   					fileToDelete = tempFileName;
   				}
   				catch(Exception moveEx)
  @@ -960,7 +1001,10 @@
   				// Try to delete the file (either the original or the moved file)
   				try
   				{
  -					System.IO.File.Delete(fileToDelete);
  +					using(SecurityContext.Impersonate(this))
  +					{
  +						System.IO.File.Delete(fileToDelete);
  +					}
   					LogLog.Debug("RollingFileAppender: Deleted file [" + fileName + "]");
   				}
   				catch(Exception deleteEx)