I finally have enough blog entries about various portions of IIS6 request processing that I can stitch together this meta-blog-entry explaining how it all works together and then apply it towards an issue. You probably want to keep a link to this loaded entry. Anyhow, here goes...
We currently has IIS 6 installed on a Windows 2003 server, and we are attempting to run some executables through the browser. We have "Scripts and Executables" selected for the web application, and we are currently allowing "All Unknown CGI Extensions" and "All Unknown ISAPI Extensions" under Web Service Extensions during testing.
With this setup we are able to run some executables successfully through a browser, like:
PingHostnamenetstatquery session
However other executables are not working:
NetDirquotaFilescrnFsutil quota
We are getting no value for most of these, except "Fsutil quota" which is returning:
The FSUTIL utility requires that you have administrative privileges
I have confirmed that the browser is using an ID that has administrative privileges by returning the userid it is using. This administrative ID does not have any problem running Fsutil quota on the server directly.
What privileges am I missing? Does something else need to be set up, or is it possible the executables not working are not CGI or ISAPI?
Thanks for any help.
Ok, this blog entry is unique in that it is the first time that I am aggregating information from all my prior posts and applying it to resolve a problem. I will NOT explain everything in detail - only the new stuff... for everything else, if I pulled the information from a blog entry, it will be linked in this blog entry somewhere appropriate.
When it comes to request execution on IIS6, this is how I think about things:
Whew. If you got to here and read through all the associated blog entries, you have just gone through a LOT of details about how IIS6 functions to process requests as well as how it interacts with user identity. Time to put this info to good use and apply it. :-)
Ok, the following details and conclusions all come from the aforementioned blog entries. If you get lost, I suggest you read 'em and read 'em again until you understand it. :-)
The first thing that needs to be clear is HOW you are "running executables through the browser". Since IIS only recognizes static files, CGI/ISAPI, or Scripts (ok, I am simplifying DAV away since it is not involved in this picture), there are two basic ways to run executables:
If you want IIS to directly run the executable as a CGI or ISAPI, then you need to configure "Scripts and Executables" execute permission as well as Web Service Extension for the binary. i.e.
If you want to run executables on IIS from a script (i.e. an ASP, ASP.Net, or PHP page is considered a script resource executed by ASP.DLL, ASPNET_ISAPI.DLL, or PHP-CGI.EXE / PHPISAPI.DLL Script Engine, respectively), then you need to configure "Scripts" execute permission as well as Web Service Extension for the appropriate Script Engine. i.e.
MyScript.asp contains the following content which executes FSUTIL.EXE:
<% set objShell = Server.CreateObject( "WScript.Shell" ) objShell.Run( "FSUTIL.EXE" ) %>
At this point, a couple differences should jump out at you:
I do not know if FSUTIL.EXE has special code that cares about the above details. But I do know that CMD.EXE does... so YMMV.
As for the question of whether the executables "not working" are not CGI/ISAPI... After executing a request handler, IIS expects a valid HTTP response to come back so that it can send it back to the requesting client.
CGI is nothing more than an executable which outputs text which conforms to the CGI specification (this blog entry describes an interest interaction between CGI and HTTP). If the output conforms to specification, IIS sends it back as-is; otherwise, IIS returns a 502 response.
Since IIS6 in WS03SP1 is lax about CGI conformance and no longer requires the status: and content-type headers, if the executable happens to output a CRLF before printing data, that data gets interpreted as response entity by IIS and sent back to the client as-is. The following are some illustrative examples with CRLF clearly marked as \r\n.
The syntax of this command is:\r\n \r\n \r\n NET [ ACCOUNTS | COMPUTER | CONFIG | CONTINUE | FILE | GROUP | HELP |\r\n HELPMSG | LOCALGROUP | NAME | PAUSE | PRINT | SEND | SESSION |\r\n SHARE | START | STATISTICS | STOP | TIME | USE | USER | VIEW ]\r\n
\r\n Active Connections\r\n \r\n Proto Local Address Foreign Address State\r\n TCP TEST-MACHINE:80 0.0.0.0:80 ESTABLISHED\r\n
As you can see, NET.EXE fails because its CGI output starts off with what looks like an invalid header named "The syntax of this command is:" (it has spaces in it, and no header value) prior to the double CRLF, so it fails to parse correctly and IIS returns a 502 "Bad Gateway" response. Meanwhile, PING.EXE succeeds because it starts off with a CRLF that tells IIS to use a 200 OK response header ahead of the PING output.
Now, if you prefix the resources with NPH- (i.e. nph-netstat.exe or nph-ping.exe), then IIS will NOT process the CGI output and just send it as-is back to the client. That is what NPH means - Non-Parsed Header. Of course, this avoids the 502 response from IIS, but if the output is not proper HTTP, it will now confuse the client, which can have its own arbitrary behavior...
Basically, these console commands are not designed to produce output that conforms to CGI specification, so the fact that they work when invoked as CGI by IIS is purely random, mostly depending on whether the output begins with something that looks like broken HTTP headers or not.
I congratulate you on getting to the end of this marathon of a blog post. I finally got a juicy topic to explain and a lot of blog entries to logically stitch together. :-)
Hopefully, this clears up common confusions surrounding:
//David