I ran into an interesting behavior when I was at OzFox giving my ASP.NET COM Interop talk. I was in the middle of demonstrating a COM component called from an ASP.NET page that accesses SQL Server. I had some trouble with the COM component running under an unexpected account for connecting to SQL Server. I usually set up my demos to use trusted connections so that I don’t have to show passwords, although in typical applications I normally use SQL Server authentication with username and password.
The demo didn’t work. It failed making connections to SQL Server. Luckily the COM object was echoing back the failures and it quickly turned out that the account the COM component was running under was IUSR_ instead of the expected NETWORK SERVICE.
Anyhow, the idea is that when you call a COM component from ASP. Net you would expect the component to inherit the user account that the ASP. Net page is running under. Typically this will be the ASPNET/NETWORK SERVICE account. A day before the demo I was experimenting with various various authentication schemes and actually changed my web.config for this sample project to use Impersonation:
<configuration>
<system.web>
<authentication mode="Windows" />
<identity impersonate="true" />
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</configuration>
Which allows all users through by using Authentication. In theory, impersonation should cause the ASP.NET page run under this account and when calling a COM component pass on this impersonation to the COM object.
When I called my COM component and echoed back the following:
Function GetRequestInfo(llForceToSystem as Boolean) as String
DECLARE Integer GetUserName ;
IN WIN32API AS GUserName ;
STRING @nBuffer, ;
INTEGER @nBufferSize
lcUserName=space(255)
lnLength=len(lcUserName)
lnError=GUserName(@lcUserName,@lnLength)
IF lnLength < 2
RETURN ""
ENDIF
lcUserName = SUBSTR(lcUsername,1,lnLength-1)
lcPath = SYS(5) + CURDIR()
lcOutput = "Current Path: " + lcPath + "<br>" +;
"Current User: " + lcUserName + " - " + SYS(0) + "<BR>" + ;
"Current Thread: " + TRANSFORM( Application.ThreadId) + "<br>"
RETURN lcOutput
I then have an ASPX page that echos back this data and ASP. Net’s internal security format:
<table id="Table1" style="WIDTH: 700px; HEIGHT: 88px" cellspacing="1"
border="1" cellpadding="3">
<tr>
<td width="120px" valign='top'>Fox Data:</td>
<td valign="top"><%= this.FoxSettings %></td>
</tr>
<tr>
<td>Environment User Account:</td>
<td><%= Environment.UserName %></td>
</tr>
<tr>
<td>Logged on User:</td>
<td><%= Request.ServerVariables["LOGON_USER"] %></td>
</tr>
</table>
where this.FoxSettings contains the result from the above COM method:
|
Fox Data: |
Current Path: D:\WINDOWS\SYSTEM32\INETSRV\ Current User: NETWORK SERVICE - RASNOTEBOOK # NETWORK SERVICE Current Thread: 476 |
|
Environment User Account: |
IUSR_RASNOTEBOOK |
|
Logged on User: |
|
Impersonation On, ASPCOMPAT not set
It’s an interesting result: The above would return NETWORK SERVICE for the account my COM server is running under while the ASP. Net page itself was running under the impersonated account – in this case the Anynomous IUSR_ account for my machine. This means that the Impersonation is not forwarded to the COM object.
This code actually worked although it shouldn't have since IUSR_ doesn't have rights to the database! What's going on? I had accidentally removed the ASPCOMPAT=”true” from this Information page. ASPCOMPAT is required for calling VFP or other STA style COM objects from an ASP.NET page. This setting among other things makes sure that the page will run on an STA threading model thread, rather than on a standard MTA thread. ASPCOMPAT affects a number of different things, including the way you can reference the Service Context (to allow you to retrieve Request, Response, Server objects from within your COM code), so if you’re using a VFP component you want to make very sure you remember this flag. If you don’t you will see some very odd behavior once more than one server is running concurrently.
So ,the above result essentially occurs when I call a COM component WITHOUT the special considerations ASP.Net for ASPCOMPAT mode.
When ASPCOMPAT mode is switched back on the impersonation actually behaves the way you would expect it to:
|
Fox Data: |
Current Path: D:\WINDOWS\SYSTEM32\INETSRV\ Current User: IUSR_RASNOTEBOOK - RASNOTEBOOK # IUSR_RASNOTEBOOK Current Thread: 476 |
|
Environment User Account: |
IUSR_RASNOTEBOOK |
|
Logged on User: |
|
Impersonation On, ASPCOMPAT set
The impersonation is being forwarded from the page into the COM object in this scenario. I expect that this is the behavior you’d want given that impersonation is enabled in the COM object.
The problem with my demo was of course that all Demo pages WERE in fact using ASPCOMPAT and my tweak of web.config to enable Impersonation caused them all to fail. So Sql Server was trying to authenticate IUSR_ as a trusted connection which didn’t work.
Now try and figure that out in the middle of a Session <g>. I got close actually. I cheated and added a logon for IUSR_ to the app and the rest of the demos worked, but of course that nags. The way it should be is like this:
|
Fox Data: |
Current Path: D:\WINDOWS\SYSTEM32\INETSRV\ Current User: NETWORK SERVICE - RASNOTEBOOK # NETWORK SERVICE Current Thread: 476 |
|
Environment User Account: |
NETWORK SERVICE |
|
Logged on User: |
|
Impersonation Off, ASPCOMPAT ON or OFF
The moral of the story – besides the obvious: don’t mess with the settings before a demo – that you should always be careful to remember using ASPCOMPAT. In general using Impersonation with ASP.NET is not a great strategy especially if you’re dealing with anonymous users.
But, impersonation can be a good choice for administration portions of a Web Site. I frequently use impersonation for the ‘Admin’ directory of my Web Site to easily block access to that part of the site and to allow admin users to gain elevated rights to perform tasks that otherwise would not be available to the ASP. Net account running under Network Service. But in all these cases I usually isolate this to a specific directory.
While we’re here – COM components generally will run under the NETWORK SERVICE (under IIS6) or ASPNET (IIS5) accounts by default. These accounts don’t have a lot of rights and you need to either make sure that your COM object has launch rights by these accounts. Same goes to any data you access locally (if you’re using VFP data) both via OleDb or from the COM object. If this is a problem (and it probably should be!) you should seriously consider sticking your COM component into a COM+ application. COM+ applications can be configured to run under an identity so you can isolate the COM object from the rest of the application without having to change permissions elsewhere. This isn’t a perfect solution but usually a lot cleaner and also safer than loosening permissions on directories and files.