Can ISAPI Extension override its user impersonation?


I usually want a high ratio of answer content to question content, but the following question is going to be pretty hard… because the question is lengthy with lots of good detail which make it easy for me to come up with the right answer without a lot of explanation. Well, here goes…


Question:


Web server environment:
Windows 2003 Server SP1 (Standard)
Default IIS installation
ActiveState Perl v5.8.7 (using the ISAPI extension rather than Perl.exe)
Third-party Perl web app


I will be running several cgi apps on our new web server and chose to use the new application pool feature in IIS 6.0 to isolate each one (for both performance and security reasons).  To test out how all this was going to work, I setup my first application pool and designated one of our perl apps to this pool.  In configuring the pool, I specified a custom user account that I had setup for this particular app (instead of using the built-in Network Service account).  I added this new account to the IIS_WPG group and gave it adequate permissions to the perl app’s directories (and the ActiveState perl runtimes).  I assumed that all client requests received by the worker process of this pool would be carried out in the context of the worker process’s identity.  But after a few attempts at running the app via my browser (anonymous connection), it was failing due to permissions errors.  I used a file monitor to see what the issue was, and I could see that the worker process was trying to create a file in my perl application’s directory and was being denied.  After closer inspection, I could see that it was because the anonymous user account was being used. 


I found your blog and of course my answer – impersonation by default.  I then used the adsutil.vbs script and MS instructions to set CreateProcessAsUser to False in the metabase.  I restarted IIS and confirmed the new parameter by opening the metabase file in a text editor.  However, upon running my app again, it did the same thing – the worker process assigned to my app pool was still trying to create this file using the anonymous IUSR_MACHINE account.  Any ideas?  The only way to get things to work is to give the anonymous user write permissions to the necessary web app directories which seems to be counter-intuitive (and not secure, unless I’m missing the big picture).  Is it possible that the ISAPI extension can override the impersonation setting?  Couldn’t find anything regarding this on ActiveState’s site.


Answer:


Ok, in this case, you have pretty much located all of the possible answers (for the benefit of the new reader, the blog referenced by the question is here).


Yes, ISAPI extension can override IIS user impersonation, but that depends on the ISAPI.


CreateProcessAsUser only applies to CGI EXE since IIS has to launch a new process to run the CGI EXE. It basically changes how IIS calls the CreateProcessAsUser() Win32 API. Meanwhile, ISAPI DLLs are different because to execute them, they are dynamically loaded into the IIS process itself (that’s why they are called Dynamic Link Library – DLL). Thus, there is no new process being created, so CreateProcessAsUser cannot apply.


Unfortunately, there is no IIS configuration to control whether IIS impersonates or uses process identity when executing ISAPI. IIS always uses process identity to run ISAPI Filter DLLs, and always uses impersonated identity to run ISAPI Extension DLLs. Before you ask – no, ISAPI Filter and ISAPI Extension are not interchangeable – they are very different beasts.


Now, I think the IIS configuration for running ISAPIs is pretty reasonable because:



  • ISAPI Filter does not “own” any request (so which user token to impersonate?) and it would be horrible performance to impersonate on every single filter event
  • ISAPI Extension “owns” a request and should run as the authenticated user identity. If it wants to control the identity it runs as, it should control it itself.

Answers…


Now, I think your real question is how to control the user identity which executes the perl script, and that comes down to two basic questions:



  1. How to control whether IIS executes user code using process identity or impersonated identity
  2. How to control the process identity and impersonated identity

As I have said earlier in the linked blog entry, IIS executes user code either through a CGI EXE, ISAPI Filter, or ISAPI Extension. ISAPI Filter always runs as process identity, ISAPI Extension always runs as impersonated identity, and CGI EXE has a switch to determine whether it runs as process or impersonated identity.


IIS defaults to impersonated identity, and user code can always access process identity by calling RevertToSelf(). I have no idea if ActiveState has such an implementation in their Perl ISAPI because it would allow Perl to run as either process or impersonated identity.


Process Identity is also easy to control on IIS6 – use the custom Application Pool Identity feature. Impersonated identity is also easy to control – for anonymous authentication, it is the configured anonymous user account; for any other authentication protocol, it is the authenticated NT user account.


I think in your case, since you are using the ISAPI Extension version of perl but need the perl scripts to run with a certain user a ccount, you need to control the impersonated identity. With anonymous authentication, you can do exactly that. You just need to configure the anonymous user identity in the virtual directory holding the perl scripts, and all anonymous requests to the perl scripts through that virtual directory will use your configured anonymous user identity. If you want even better control, consider using IIsWebFile to set the anonymous user identity on a per perl-script basis (see my other blog post on IIsWebFile on how this works).


However, realize that code in an ISAPI can ALWAYS get access to the process identity by calling RevertToSelf(), including from the Perl script itself. Thus, you should see that IIS uses Application Pool Identity more as an isolation mechanism than an execution mechanism.


//David

Comments (4)

  1. Brian Treff says:

    Thanks for the reply David. I knew that worker processes linked to ISAPI extension dlls rather than spawning new processes as with CGI, but I failed to make the connection between using CreateProcessAsUser (keyword being "Process") and the fact that there was no new process being launched. It makes perfect sense in hindsight, as always. Thanks for clearing things up. It looks like I will have to give the anonymous user more rights that I had initially planned – I’ll just do so sparingly.

  2. David Wang says:

    You can consider configuring a different anonymous user account for the /cgi-bin directory that holds the Perl scripts so as to not affect anonymous user access elsewhere in your website.

    //David

  3. sani chabi yo says:

    First im using IIS 6.00

    Im trying to download a file in my ISAPI extension.

    char user[255];

    DWORD cchBuff = 255;

    GetUserName(user,&cchBuff);

    fd<<"user = "<<user<<endl;

    HANDLE userToken;

    if(pCtxt->m_pECB->ServerSupportFunction(pCtxt->m_pECB->ConnID,HSE_REQ_GET_IMPERSONATION_TOKEN,(LPVOID)&userToken,NULL,NULL))

    if(ImpersonateLoggedOnUser(userToken))

    fd<<"youpi impersonation succed"<<endl;

    else

    fd<<"Oh no impersonation fail"<<endl;

    GetUserName(user,&cchBuff);

    fd<<"user = "<<user<<endl;

    if(ImpersonateLoggedOnUser(userToken))

    fd<<"youpi impersonation succed"<<endl;

    else

    fd<<"Oh no impersonation fail"<<endl;

    hInternet = pobjHttp.OpenInternet() ;

    hConnection = pobjHttp.OpenConnection(hInternet,temp) ;

    pobjRes = pobjHttp.RequestGet(hInternet,hConnection,temp) ;

    if(pobjRes==NULL )

    {

    fd<<"error in RequestGet"<<endl;

    *pCtxt <<"pobjRes == NULL n<br>";

    fd.close();

    return;

    }

    Like you see the GetUserName() function return the correct user, but the file canoot be download, It return an unauthorisation error (401). But the same code in a windows application works fine.

    The application pool identity is NETWORK SERVICE.

    Am I missing something? Please help.

  4. David Wang says:

    sani chabi yo – Yes, what you are missing is Security 101.

    Your problem has nothing to do with impersonation and everything to do with delegation of an authenticated user identity. Ability to delegate depends on the authentication protocol as well as server/domain (i.e. Trust) configuration. Let me explain.

    By definition, IIS has already impersonated the remote authenticated user prior to calling your ISAPI Extension, so your calls to HSE_REQ_GET_IMPERSONATION_TOKEN and ImpersonateLoggedOnUser() are duplicate and unnecessary.

    The real problem is that your ISAPI is trying to take the remotely authenticated identity and RE-USE it to make another HTTP network call. This is the classic "double hop" scenario, and the results on this second hop (i.e. the .RequestGet() call ) completely depends on whether the origin server is configured for delegation.

    How I explain "double hop" is this:

    Suppose the remote user authenticated to IIS and your ISAPI runs. Should your ISAPI be able to reuse that remote user’s credentials to contact that user’s bank, authenticate, and then electronically transfer all their money into your bank account? If you agree, then your call to RequestGet() should succeed and you are a thief. If you disagree, then your call to RequestGet() should fail with 401.

    The reason why the same code works in a Windows application is because there is no "double hop" – it is a single hop made by the logged on user who is directly taking the action to make a network call.

    As for Authentication protocols and delegation:

    – Basic is insecure and implicit delegation. Thus it automatically works in double-hop scenario because server has username:password and can reuse it. This is totally insecure because server can act as the client without their consent (i.e. with Basic auth, your ISAPI would be able to contact that user’s bank and withdraw all their money).

    – Windows (NTLM) – does not support delegation because it is connection bound. The second hop is over a different connection (the first hop is authenticated between the client and your server), so server cannot reuse the user’s credentials. This is secure because server cannot act as the client.

    – Windows (Kerberos) – supports delegation implicitly as a part of the protocol. Trust is established between the servers via Active Directory membership. You have to configure the server for delegation, though.

    I recommend that you:

    1. Read at least this blog entry to understand the security concepts that you are missing (I have several other blog entries fleshing out this concept through examples and not definitions):

    http://blogs.msdn.com/david.wang/archive/2005/07/06/SSO_ISAPI_Considerations_2.aspx

    2. Read this URL on how delegation is configured for a UNC share, which is nearly identical to your situation except you have custom ISAPI code doing resource transfer:

    http://www.microsoft.com/technet/prodtechnol/windowsserver2003/technologies/webapp/iis/remstorg.mspx

    //David