Search code examples
.netweb-servicesimpersonationkerberoswse

How do I get a WSE 3.0 web service to impersonate my client's identity?


I have a WSE 3.0 based web service running in Windows Server 2003 under IIS 6.0. I want the web service process to impersonate the client user that sends the web service request, however the service is not impersonating the client.

The web application has its own app pool, which is currently set to run under the Network Service identity. The Windows Server 2003 machine account has been enabled for delegation in Active Directory (at least according to my IT guy). The service WSE policy (in wse3policyCache.config) looks like this:

<policy name="GeneratedServicesPolicy">
    <kerberosSecurity establishSecurityContext="false" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" ttlInSeconds="300">
        <protection>
            <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
            <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
            <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
        </protection>
     </kerberosSecurity>
     <requireActionHeader />
</policy>

The service has the following entries (among others) in its web.config:

<identity impersonate="false"/>
<authentication mode="Windows"/>

Anonymous access is enabled for the application (this is required since transport-level security is not used by the service, message-leve is). The machine account has the following SPN's registered:

HOST/RD360-2
HOST/rd360-2.mycompany.com

The client has the following in its wse3policyCache.config:

<policy name="KerbClient">
    <kerberosSecurity establishSecurityContext="false" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" ttlInSeconds="300">
        <token>
            <kerberos targetPrincipal="HOST/rd360-2.mycompany.com" impersonationLevel="Impersonation" />
        </token>
        <protection>
            <request signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
            <response signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
            <fault signatureOptions="IncludeAddressing, IncludeTimestamp, IncludeSoapBody" encryptBody="false" />
        </protection>
    </kerberosSecurity>
    <requireActionHeader />
</policy>

The client code looks like this:

static void Main(string[] args)
{
    AddFloatsWSWse client = new AddFloatsWSWse();
    client.SetPolicy("KerbClient");

    double result = client.AddFloats(2.3, 3.2);
    Console.WriteLine("Result was: '" + result + "'");
}

The service is not impersonating my client identity, though. I am using log4net in the service, and when I ask it to print out the %username into the ASP.NET trace log, it is always NT AUTHORITY\NETWORK SERVICE and not the client user id. Is there anything I'm doing wrong? Is there somewhere I can look to see if WSE is even trying to perform the impersonation and failing? I do see the following entries in my event log (MYDOMAIN and MYUSER are obscured here):

Event Type: Success Audit
Event Source:   Security
Event Category: Privilege Use 
Event ID:   576
Date:       12/9/2009
Time:       11:07:16 AM
User:       MYDOMAIN\MYUSER
Computer:   RD360-2
Description:
    Special privileges assigned to new logon:
        User Name:  MYUSER
        Domain:     MYDOMAIN
        Logon ID:       (0x0,0x4B410AE)
        Privileges: SeSecurityPrivilege
                SeBackupPrivilege
                SeRestorePrivilege
                SeTakeOwnershipPrivilege
                SeDebugPrivilege
                SeSystemEnvironmentPrivilege
                SeLoadDriverPrivilege
                SeImpersonatePrivilege

----------------------------------------------------------------------------------

Event Type: Success Audit
Event Source:   Security
Event Category: Logon/Logoff 
Event ID:   540
Date:       12/9/2009
Time:       11:07:16 AM
User:       MYDOMAIN\MYUSER
Computer:   RD360-2
Description:
    Successful Network Logon:
        User Name:  MYUSER
        Domain:     MYDOMAIN
        Logon ID:   (0x0,0x4B410AE)
        Logon Type: 3
        Logon Process:  Kerberos
        Authentication Package: Kerberos
        Workstation Name:   
        Logon GUID: {OBFUSCATED}
        Caller User Name:   -
        Caller Domain:  -
        Caller Logon ID:    -
        Caller Process ID: -
        Transited Services: -
        Source Network Address: -
        Source Port:    -

And in my WSE trace file I do see:

<processingStep description="Entering SOAP filter Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter" />
<processingStep description="Exited SOAP filter Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter" />
<processingStep description="Entering SOAP filter Microsoft.Web.Services3.Design.KerberosAssertion+ServiceInputFilter" />
<processingStep description="Exited SOAP filter Microsoft.Web.Services3.Design.KerberosAssertion+ServiceInputFilter" />

so at least I know the Kerberos extensions are processing my Kerberos headers.

Edit: My webservice then uses a proprietary client communication library to call into another server using SSPI/IWA (let's call this third server a foo server). I want it to use the client's identity when it makes this second call into the foo server. This means that this client communication library calls AcquireCredentialsHandle and InitializeSecurityContext using the SPN of the foo server and a different service. In this particular case, the foo server is actually running on the same machine as the WSE web service (so it's using the SPN mycompany/rd260-2). Since this second hop is to the same machine, I would expect this to use NTLM, but it still should impersonate my web service client's user identity, shouldn't it? In the logs of the foo server, I see that it accepts the connection, uses the IWA security context provided and tells me that based on this security context, the connecting user is rd36-2$ which is the machine account since the WSE web service is running in IIS under the Network Service identity (which is in turn associated to the machine account). In the logs of the foo server, after it receives the IWA security context, I ultimately want to see the identity of the user who submitted the web service request. Would it be useful to move the foo server to a different machine to see if that has some bearing on this?


Solution

  • The Kerberos token used with WSE 3 is a message-level security mechanism and only authenticates the client. It does not actually change the security context as in IWA, so you won't notice anything different in the trace log as such. In order to actually impersonate the client, you have to:

    • Enable impersonation on the security token with impersonationLevel="Impersonation" on the <kerberos> element (which you've already done); and
    • Have your WebMethod create a WindowsImpersonationContext based on the token's identity.

    Example:

    WindowsIdentity identity =
        (WindowsIdentity)RequestSoapContext.Current.IdentityToken.Identity;
    WindowsImpersonationContext impersonationContext = null;
    try
    {
        impersonationContext = identity.Impersonate();
    
        // Perform your work here
        // ...
    }
    finally
    {
        if (impersonationContext != null)
        {
            impersonationContext.Undo();
        }
    }