Issue with ASP.NET client, COM Interop, and Identity impersonation

  • Thread starter Thread starter Anil Krishnamurthy
  • Start date Start date
A

Anil Krishnamurthy

We have an ASP.NET application that uses COM objects through Interop. The
web application requires access to network and database resources and hence,
needs to impersonate a domain account. The problem is that even when it is
configured to run under a certain identity through Web.config, the
impersonation is not carried through to COM library. Consequently, the code
in COM object runs under a local account and any code that needs to access
network will not work correctly.

ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)

We tried to solve the problem by impersonating the caller in COM library.
Instead of using CoImpersonateClient(), which is required for every method,
we tried using a different approach so that impersonation is in effect
beyond function call. It was implemented as follows:

HRESULT CClientImpersonator::Impersonate()
{
BOOL bOk = FALSE;
HRESULT hr = E_FAIL;
DWORD dwErr = 0;

// try to impersonate the client
hr = ::CoImpersonateClient();
if ( SUCCEEDED( hr ) )
{
HANDLE hToken = NULL;
HANDLE hDupToken = NULL;

// get the thread's impersonation token
bOk = ::OpenThreadToken( ::GetCurrentThread(), TOKEN_ALL_ACCESS,
TRUE, &hToken );
if ( TRUE == bOk )
{
// dup it
bOk = ::DuplicateTokenEx( hToken, TOKEN_ALL_ACCESS, NULL,
SecurityImpersonation, TokenImpersonation, &hDupToken );
if ( TRUE == bOk )
{
// switch back the identity
hr = ::CoRevertToSelf();
if ( SUCCEEDED(hr) )
{
// now impersonate the same identity so it 'sticks'
beyond function call level
bOk = ::ImpersonateLoggedOnUser( hDupToken );
if ( FALSE == bOk )
{
hr = HRESULT_FROM_WIN32( ::GetLastError() );
ATLTRACE( "ImpersonateLoggedOnUser() failed.
GetLastError() returned %8x.\n", hr );
}
}
else
{
ATLTRACE( "CoRevertToSelf() failed. Error code %8x.\n",
hr );
}
}
else
{
hr = HRESULT_FROM_WIN32( ::GetLastError() );
ATLTRACE( "DuplicateTokenEx() failed. GetLastError()
returned %8x.\n", hr );
}
}
else
{
hr = HRESULT_FROM_WIN32( ::GetLastError() );
ATLTRACE( "OpenThreadToken() failed. GetLastError() returned
%8x.\n", hr );
}

::CloseHandle( hDupToken );
::CloseHandle( hToken );
}
else
{
if ( hr != RPC_E_CALL_COMPLETE )
ATLTRACE( "CoImpersonateClient() failed. Error code: %8x.\n",
hr );
else
hr = S_FALSE; // ignore the failure when the context is not
available
}
return hr;
}

Unfortunately, the solution does not seem to work on all machines. In cases
where it does not work, the behavior is as follows: Instantiation of COM
object succeeds but attempt to invoke any method or access any property on
the same object fails. The error is reported as
"System.InvalidCastException. QueryInterface for interface <Interface name>
failed"

Any ideas?

Thanks
 
Anil Krishnamurthy said:
We have an ASP.NET application that uses COM objects through Interop. The
web application requires access to network and database resources and
hence,
needs to impersonate a domain account. The problem is that even when it is
configured to run under a certain identity through Web.config, the
impersonation is not carried through to COM library. Consequently, the
code
in COM object runs under a local account and any code that needs to access
network will not work correctly.

How is that possible? if asp.net is running in a domain identity context AND
you don't have impersonation enabled, the in-proc COM object should run with
the same domain user's credentials, where else would the local account
identity come from?

Willy.
 
you need to set aspcompat=true (turn off thread agility) to lock down the
thread calling the com component, then set the domain account and password
in the web.config.

note: there is a small performance cost for aspcompat=true

-- bruce (sqlwork.com)
 
Willy Denoyette said:
How is that possible? if asp.net is running in a domain identity context AND
you don't have impersonation enabled, the in-proc COM object should run with
the same domain user's credentials, where else would the local account
identity come from?

Impersonation is enabled in Web.config and it is set to use a domain
account. But on COM object side, when I try to get the user name, it is
IUSR_MachineName and that is not what I want. Also, I cannot use
AspCompat="true".

Anil
 
With impersonation enabled::
- your thread will impersonate the anonymous account (IUSR_MachineName in
your case) if the caller (browser client) is unauthenticated.
- if the client is authenticated, the thread impersonates this account.
In both cases, the impersonation token has no network access privileges, so
this won't help you to access non local resources (unless you have Kerberos
delegation enabled and set-up correctly, but that's another story).

With impersonation turned off, all threads in the asp.net worker process
use the process token when accessing remote resources, and because your
in-proc COM object runs in the same security context of the caller (the
executing thread) it will use the same token, problem solved.

About your "aspcompat" remark. If your COM object is a threadingmodel =
apartment type object (STA) you better run in "aspcompat" mode.
If you don't, your object will run on the default STA thread provided by the
asp.net worker process, this will negatively impact the performance as all
calls have to get marshaled.
Just curious, why can't you set aspcompat=true?

Willy.
 
We have an ASP.NET application that uses COM objects through Interop. The
web application requires access to network and database resources and hence,
needs to impersonate a domain account. The problem is that even when it is
configured to run under a certain identity through Web.config, the
impersonation is not carried through to COM library. Consequently, the code
in COM object runs under a local account and any code that needs to access
network will not work correctly.

The issue is often resolved by moving code that uses COM into the out of
process serviced component, which works under different (from ASP's)
credentials.
 
Willy Denoyette said:
With impersonation turned off, all threads in the asp.net worker process
use the process token when accessing remote resources, and because your
in-proc COM object runs in the same security context of the caller (the
executing thread) it will use the same token, problem solved.

When impersonation is turned off, the identity is ASPNET and since that is a
local account, how can you access remote resources?
About your "aspcompat" remark. If your COM object is a threadingmodel =
apartment type object (STA) you better run in "aspcompat" mode.
If you don't, your object will run on the default STA thread provided by the
asp.net worker process, this will negatively impact the performance as all
calls have to get marshaled.
Just curious, why can't you set aspcompat=true?

Actually, the web application team informed me that they have switched to
AspCompat mode and it does not help much. The problem is that the call to
COM object comes from Java script and identity impersonation does not work
in this case. I mentioned that AspCompat flag could not used because there
is another application, a web service, that uses the same set of COM
objects.

Anil
 
ASPNET, that means that your config file is not like you said in your
original post.
/ snip
ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)
/end snip

Here you say that asp.net runs as (Domain\NetworkUser), but this is not the
case. So please change your web.config file to run the worker process as
Domain\NetworkUser.

Willy.
 
I was trying to use that diagram to describe the problem and looks like
there was some problem formatting it properly.

This is what the web.config looks like

<identity impersonate="true" userName="Domain\UserName"
password="password'/>

When I print the identities on ASP.NET side and COM object side, this is
what I get.

[ASP.NET]
Domain\UserName

[COM]
Machine\IUSR_Machine

When I switch off impersonation, the identity on COM side is

[COM]
Machine\ASPNET

Hope I have made it clear now ;) So, either way, the code in COM object runs
under a local machine account and thus, cannot access network resource.

Thanks
Anil
 
When you need to run the asp.net worker process under a specific domain
account (not aspnet), you have to change the processModel attributes
userName and password in your machine.config file (not web.config).

userName=="Domain\UserName", password="...."

Willy.



Anil Krishnamurthy said:
I was trying to use that diagram to describe the problem and looks like
there was some problem formatting it properly.

This is what the web.config looks like

<identity impersonate="true" userName="Domain\UserName"
password="password'/>

When I print the identities on ASP.NET side and COM object side, this is
what I get.

[ASP.NET]
Domain\UserName

[COM]
Machine\IUSR_Machine

When I switch off impersonation, the identity on COM side is

[COM]
Machine\ASPNET

Hope I have made it clear now ;) So, either way, the code in COM object
runs
under a local machine account and thus, cannot access network resource.

Thanks
Anil

Willy Denoyette said:
ASPNET, that means that your config file is not like you said in your
original post.
/ snip
ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)
/end snip

Here you say that asp.net runs as (Domain\NetworkUser), but this is not the
case. So please change your web.config file to run the worker process as
Domain\NetworkUser.

Willy.
 
Yes, but that would affect all applications. We cannot do that especially on
client machines. We need impersonation to work only for this web
application.

The problem is that the approach to impersonation through retrieval of the
thread's token and calling ImpersonateLoggedOnUser() works on my machine
only but not on other machines. ;( We are unable to understand the reason
for that. We have tried to compare the user permissions, settings etc. but
no avail.

Anil

Willy Denoyette said:
When you need to run the asp.net worker process under a specific domain
account (not aspnet), you have to change the processModel attributes
userName and password in your machine.config file (not web.config).

userName=="Domain\UserName", password="...."

Willy.



Anil Krishnamurthy said:
I was trying to use that diagram to describe the problem and looks like
there was some problem formatting it properly.

This is what the web.config looks like

<identity impersonate="true" userName="Domain\UserName"
password="password'/>

When I print the identities on ASP.NET side and COM object side, this is
what I get.

[ASP.NET]
Domain\UserName

[COM]
Machine\IUSR_Machine

When I switch off impersonation, the identity on COM side is

[COM]
Machine\ASPNET

Hope I have made it clear now ;) So, either way, the code in COM object
runs
under a local machine account and thus, cannot access network resource.

Thanks
Anil

Willy Denoyette said:
ASPNET, that means that your config file is not like you said in your
original post.
/ snip
ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)
/end snip

Here you say that asp.net runs as (Domain\NetworkUser), but this is not the
case. So please change your web.config file to run the worker process as
Domain\NetworkUser.

Willy.


message
With impersonation turned off, all threads in the asp.net worker process
use the process token when accessing remote resources, and because
your
in-proc COM object runs in the same security context of the caller
(the
executing thread) it will use the same token, problem solved.

When impersonation is turned off, the identity is ASPNET and since
that
is
a
local account, how can you access remote resources?

About your "aspcompat" remark. If your COM object is a threadingmodel
=
apartment type object (STA) you better run in "aspcompat" mode.
If you don't, your object will run on the default STA thread
provided
by
the
asp.net worker process, this will negatively impact the performance as
all
calls have to get marshaled.
Just curious, why can't you set aspcompat=true?

Actually, the web application team informed me that they have
switched
to
AspCompat mode and it does not help much. The problem is that the
call
to
COM object comes from Java script and identity impersonation does not work
in this case. I mentioned that AspCompat flag could not used because there
is another application, a web service, that uses the same set of COM
objects.

Anil


in
message
message

How is that possible? if asp.net is running in a domain identity
context
AND
you don't have impersonation enabled, the in-proc COM object should
run
with
the same domain user's credentials, where else would the local account
identity come from?


Impersonation is enabled in Web.config and it is set to use a domain
account. But on COM object side, when I try to get the user name,
it
is
IUSR_MachineName and that is not what I want. Also, I cannot use
AspCompat="true".

Anil
 
Ok, back to your requirements.
"aspcompat" is not an option, and running the process under a domain account
either.
When you COM object is a threadingmodel=apartment (STA), it will be created
in a the default process STA, and use the process token when accessing
network resources, now the process runs as a local account (ASPNET) and as
such has no network access privileges.
Your only option left is:
- Spawn a new thread and set it's apartment state to STA before starting
(this solves the inter-apartment marshaling issue).
- In your threadproc. you need to get a logon token by calling Win32 API
"LogonUser" and use this token to impersonate the current thread token by
calling WindowsIdentity.Impersonate(). When done you call Undo().
Note that on W2K calling "LogonUser" requires TCB privileges (run as part
of the Operating system), something you need to consider with care, as
ASPNET will run with elevated privileges when doing so. Windows XP and W2K3
don't need TCB privileges to call "LogonUser".

Willy.

Anil Krishnamurthy said:
Yes, but that would affect all applications. We cannot do that especially
on
client machines. We need impersonation to work only for this web
application.

The problem is that the approach to impersonation through retrieval of the
thread's token and calling ImpersonateLoggedOnUser() works on my machine
only but not on other machines. ;( We are unable to understand the reason
for that. We have tried to compare the user permissions, settings etc. but
no avail.

Anil

Willy Denoyette said:
When you need to run the asp.net worker process under a specific domain
account (not aspnet), you have to change the processModel attributes
userName and password in your machine.config file (not web.config).

userName=="Domain\UserName", password="...."

Willy.



Anil Krishnamurthy said:
I was trying to use that diagram to describe the problem and looks like
there was some problem formatting it properly.

This is what the web.config looks like

<identity impersonate="true" userName="Domain\UserName"
password="password'/>

When I print the identities on ASP.NET side and COM object side, this
is
what I get.

[ASP.NET]
Domain\UserName

[COM]
Machine\IUSR_Machine

When I switch off impersonation, the identity on COM side is

[COM]
Machine\ASPNET

Hope I have made it clear now ;) So, either way, the code in COM object
runs
under a local machine account and thus, cannot access network resource.

Thanks
Anil

ASPNET, that means that your config file is not like you said in your
original post.
/ snip
ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)
/end snip

Here you say that asp.net runs as (Domain\NetworkUser), but this is
not
the
case. So please change your web.config file to run the worker process as
Domain\NetworkUser.

Willy.


in
message
message
With impersonation turned off, all threads in the asp.net worker
process
use the process token when accessing remote resources, and because
your
in-proc COM object runs in the same security context of the caller
(the
executing thread) it will use the same token, problem solved.

When impersonation is turned off, the identity is ASPNET and since that
is
a
local account, how can you access remote resources?

About your "aspcompat" remark. If your COM object is a threadingmodel
=
apartment type object (STA) you better run in "aspcompat" mode.
If you don't, your object will run on the default STA thread provided
by
the
asp.net worker process, this will negatively impact the performance as
all
calls have to get marshaled.
Just curious, why can't you set aspcompat=true?

Actually, the web application team informed me that they have switched
to
AspCompat mode and it does not help much. The problem is that the call
to
COM object comes from Java script and identity impersonation does
not
work
in this case. I mentioned that AspCompat flag could not used because
there
is another application, a web service, that uses the same set of COM
objects.

Anil


"Anil Krishnamurthy" <[email protected]>
wrote
in
message
message

How is that possible? if asp.net is running in a domain identity
context
AND
you don't have impersonation enabled, the in-proc COM object should
run
with
the same domain user's credentials, where else would the local
account
identity come from?


Impersonation is enabled in Web.config and it is set to use a domain
account. But on COM object side, when I try to get the user name, it
is
IUSR_MachineName and that is not what I want. Also, I cannot use
AspCompat="true".

Anil
 
Update.
Apparently, AspCompat mode was not being used in all the aspx pages and
thats what caused the problem from Java script. Adding it fixed the issue.

Anil

Willy Denoyette said:
Ok, back to your requirements.
"aspcompat" is not an option, and running the process under a domain account
either.
When you COM object is a threadingmodel=apartment (STA), it will be created
in a the default process STA, and use the process token when accessing
network resources, now the process runs as a local account (ASPNET) and as
such has no network access privileges.
Your only option left is:
- Spawn a new thread and set it's apartment state to STA before starting
(this solves the inter-apartment marshaling issue).
- In your threadproc. you need to get a logon token by calling Win32 API
"LogonUser" and use this token to impersonate the current thread token by
calling WindowsIdentity.Impersonate(). When done you call Undo().
Note that on W2K calling "LogonUser" requires TCB privileges (run as part
of the Operating system), something you need to consider with care, as
ASPNET will run with elevated privileges when doing so. Windows XP and W2K3
don't need TCB privileges to call "LogonUser".

Willy.

Anil Krishnamurthy said:
Yes, but that would affect all applications. We cannot do that especially
on
client machines. We need impersonation to work only for this web
application.

The problem is that the approach to impersonation through retrieval of the
thread's token and calling ImpersonateLoggedOnUser() works on my machine
only but not on other machines. ;( We are unable to understand the reason
for that. We have tried to compare the user permissions, settings etc. but
no avail.

Anil

Willy Denoyette said:
When you need to run the asp.net worker process under a specific domain
account (not aspnet), you have to change the processModel attributes
userName and password in your machine.config file (not web.config).

userName=="Domain\UserName", password="...."

Willy.



message I was trying to use that diagram to describe the problem and looks like
there was some problem formatting it properly.

This is what the web.config looks like

<identity impersonate="true" userName="Domain\UserName"
password="password'/>

When I print the identities on ASP.NET side and COM object side, this
is
what I get.

[ASP.NET]
Domain\UserName

[COM]
Machine\IUSR_Machine

When I switch off impersonation, the identity on COM side is

[COM]
Machine\ASPNET

Hope I have made it clear now ;) So, either way, the code in COM object
runs
under a local machine account and thus, cannot access network resource.

Thanks
Anil

ASPNET, that means that your config file is not like you said in your
original post.
/ snip
ASP.NET
{Web app} -------------Interop --------------->{COM Library}
(Domain\NetworkUser)
(LocalHost\IUSR_MachineName)
/end snip

Here you say that asp.net runs as (Domain\NetworkUser), but this is
not
the
case. So please change your web.config file to run the worker
process
as
Domain\NetworkUser.

Willy.


in
message
message
With impersonation turned off, all threads in the asp.net worker
process
use the process token when accessing remote resources, and because
your
in-proc COM object runs in the same security context of the caller
(the
executing thread) it will use the same token, problem solved.

When impersonation is turned off, the identity is ASPNET and since that
is
a
local account, how can you access remote resources?

About your "aspcompat" remark. If your COM object is a threadingmodel
=
apartment type object (STA) you better run in "aspcompat" mode.
If you don't, your object will run on the default STA thread provided
by
the
asp.net worker process, this will negatively impact the
performance
as
all
calls have to get marshaled.
Just curious, why can't you set aspcompat=true?

Actually, the web application team informed me that they have switched
to
AspCompat mode and it does not help much. The problem is that the call
to
COM object comes from Java script and identity impersonation does
not
work
in this case. I mentioned that AspCompat flag could not used because
there
is another application, a web service, that uses the same set of COM
objects.

Anil


"Anil Krishnamurthy" <[email protected]>
wrote
in
message
message

How is that possible? if asp.net is running in a domain identity
context
AND
you don't have impersonation enabled, the in-proc COM object should
run
with
the same domain user's credentials, where else would the local
account
identity come from?


Impersonation is enabled in Web.config and it is set to use a domain
account. But on COM object side, when I try to get the user
name,
it
is
IUSR_MachineName and that is not what I want. Also, I cannot use
AspCompat="true".

Anil
 
Back
Top