How can I allow a WCF web service which uses impersonation to write to a Windows Event Log?

520 views Asked by At

I'm writing a small WCF service and I'd like to be able to write to a custom event log. I've created an event log and source and a minimal service and the service can write to the log. Then I added impersonation and started getting 'Access is denied' errors at the client. When logged at the server, it takes the form of a 'Requested registry access is not allowed' error.

Although this wouldn't be the case with users of the service, the account being impersonated is (necessarily) a member of Administrators (in order to be able to debug using IIS).

I get the same results running the WCF Test Client on the same machine with a non-admin user, and on another machine with a non-admin user.

To recreate:

  1. Start PowerShell as Administrator
  2. New-EventLog -LogName Test42 -Source Test42.Web
  3. Close PowerShell
  4. Start Visual Studio as Administrator
  5. New Project - .NET Framework 4.5 > Visual C# > WCF > WCF Service Application 'ImpSvc1' (* Create directory for solution)
  6. Replace the contents of IService1.cs, Service1.svc and Web.config with the contents below.
  7. In the WCF Project Properties > Web (tab) > Servers - choose Local IIS and click Create Virtual Directory.
  8. Build the Project.
  9. Start IIS Manager - Select the Default Web Site
  10. Open IIS
  11. You'll need an Application Pool with a domain account (with no special permissions on the IIS machine) and integrated pipeline mode, so create one if you haven't got one.
  12. Select Default Web Site > Authentication. Ensure Windows Authentication is enabled and all others are disabled.
  13. Select ImpSvc1 > Basic Settings. Select the Application pool from above.
  14. Close IIS Manager.
  15. Start the WCF Test Client. File > Add Service > http://localhost/ImpSvc1/Service1.svc
  16. Double-click on the GetDate() method and click Invoke. You should get RESPONSE: user=; dateTime= and en event created in the Test42 event log.
  17. Uncomment the using statement, build and repeat the Invoke and you should get: RESPONSE: user=; dateTime= and en event created in the Test42 event log.
  18. Uncomment the EventLog.WriteEntry statement, build and repeat the Invoke and you should get an 'Access is denied' error in the WCF Test Client.
  19. If you start the project with Debugging, you'll see it's a 'Requested registry access is not allowed.'

IService1.cs

using System;
using System.ServiceModel;

namespace ImpSvc1
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        String GetData();
    }
}

Service1.svc

using System;
using System.Diagnostics;
using System.Security.Principal;
using System.ServiceModel;

namespace ImpSvc1
{
    public class Service1 : IService1
    {
        public String GetData()
        {
            EventLog.WriteEntry("Test42.Web", "Test message", 
                EventLogEntryType.Information, 1, 0);
            //using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
            //{
                //EventLog.WriteEntry("Test42.Web", "Test message from using statement", 
                //    EventLogEntryType.Information, 2, 0);
                return String.Format("RESPONSE: user={0}; dateTime={1}", 
                    WindowsIdentity.GetCurrent().Name, DateTime.Now);
            //}
        }
    }
}

Web.config

<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" 
         value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" 
                 targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding closeTimeout="00:05:00"
                 openTimeout="00:05:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00">
          <security mode="TransportCredentialOnly">
            <transport clientCredentialType="Windows" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" 
                           httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpBinding" 
           scheme="http" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" 
                               multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
    <security>
      <authentication>
        <anonymousAuthentication enabled="false" />
        <windowsAuthentication enabled="true" />
      </authentication>
    </security>
  </system.webServer>

</configuration>
0

There are 0 answers