?
=?iso-8859-1?q?Eir=EDkur_Fannar_Torfason?=
I'm wrestling with a problem that I'm hoping someone can help me with.
I have a web application written in VS.2003 and running on version 1.1
of the .NET Framework on XP pro and Windows server 2003 that connects
to a SQL server database and authenticates itself using windows
authentication. The web application is configured to impersonate a
local user account that has been granted access to the database.
Here's the impersonation snip from the web.config.
<identity impersonate="true" userName="<username>"
password="<password>" />
For certain long running tasks we create a new thread so that the task
can completed asynchronously. When the new thread is created it has
the user context of the local aspnet account. We therefore must let
the new thread impersonate the local user so that the thread will be
able to access the database. This works most of the time but sometimes
I get a SecurityException with the following message: 'Unable to
impersonate user'.
The most effective way for me to reproduce this error has been open
two browsers on two separate computers and point them to a page that
executes a long running task in a separate thread and refresh the
page, first on one computer and then immediately on the second
computer. I repeat this about 10-15 times and I'll usually end up with
a handful of securityexceptions (which I log in the event log).
I've found two knowledge base articles that seem to focus on this
issue. The first one is here: http://support.microsoft.com/kb/842790.
I've tried 2 out of 3 workarounds mentioned in that article and
neither helped. The third workaround, calling RevertToSelf, is not a
viable option for me since the main thread will then no longer be able
to access the database. The second article is here: http://support.microsoft.com/kb/319615
but it only applies to version 1.0 of the framework.
I've tried numerous things in a weak attemtp to fix this issue. One
thing I tried was calling the DuplicateToken method in advapi32.dll,
passing the token of the impersonated user on the main thread and
using the duplicated token for impersonation on the new thread. That
unfortunately didn't work but it might be of interest that the
DuplicateToken method failed (returned false) occasionally, probably
because of the same conditions that cause the impersonation to fail
with a securityexception.
Here's a simplified example of the code that is being executed:
public class LongRunningTask
{
private IntPtr userToken;
private WindowsImpersonationContext impersonationContext;
public void StartTask()
{
//
// This is executed on the main thread
//
IntPtr userToken = WindowsIdentity.GetCurrent().Token;
LongRunningTask task = new LongRunningTask(userToken);
Thread thread = new Thread(new ThreadStart(task.ExecuteTask));
thread.Name = "Task running thread";
thread.Priority = ThreadPriority.Lowest;
thread.IsBackground = false;
thread.Start();
}
public LongRunningTask()
{
}
public LongRunningTask(IntPtr userToken)
{
this.userToken = userToken;
}
public void ExecuteTask()
{
//
// This is executed on the new thread
//
ImpersonateCallingThread();
// ... do work
UndoImpersonation();
}
private void ImpersonateCallingThread()
{
this.impersonationContext =
WindowsIdentity.Impersonate(this.userToken);
}
private void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
}
I have a web application written in VS.2003 and running on version 1.1
of the .NET Framework on XP pro and Windows server 2003 that connects
to a SQL server database and authenticates itself using windows
authentication. The web application is configured to impersonate a
local user account that has been granted access to the database.
Here's the impersonation snip from the web.config.
<identity impersonate="true" userName="<username>"
password="<password>" />
For certain long running tasks we create a new thread so that the task
can completed asynchronously. When the new thread is created it has
the user context of the local aspnet account. We therefore must let
the new thread impersonate the local user so that the thread will be
able to access the database. This works most of the time but sometimes
I get a SecurityException with the following message: 'Unable to
impersonate user'.
The most effective way for me to reproduce this error has been open
two browsers on two separate computers and point them to a page that
executes a long running task in a separate thread and refresh the
page, first on one computer and then immediately on the second
computer. I repeat this about 10-15 times and I'll usually end up with
a handful of securityexceptions (which I log in the event log).
I've found two knowledge base articles that seem to focus on this
issue. The first one is here: http://support.microsoft.com/kb/842790.
I've tried 2 out of 3 workarounds mentioned in that article and
neither helped. The third workaround, calling RevertToSelf, is not a
viable option for me since the main thread will then no longer be able
to access the database. The second article is here: http://support.microsoft.com/kb/319615
but it only applies to version 1.0 of the framework.
I've tried numerous things in a weak attemtp to fix this issue. One
thing I tried was calling the DuplicateToken method in advapi32.dll,
passing the token of the impersonated user on the main thread and
using the duplicated token for impersonation on the new thread. That
unfortunately didn't work but it might be of interest that the
DuplicateToken method failed (returned false) occasionally, probably
because of the same conditions that cause the impersonation to fail
with a securityexception.
Here's a simplified example of the code that is being executed:
public class LongRunningTask
{
private IntPtr userToken;
private WindowsImpersonationContext impersonationContext;
public void StartTask()
{
//
// This is executed on the main thread
//
IntPtr userToken = WindowsIdentity.GetCurrent().Token;
LongRunningTask task = new LongRunningTask(userToken);
Thread thread = new Thread(new ThreadStart(task.ExecuteTask));
thread.Name = "Task running thread";
thread.Priority = ThreadPriority.Lowest;
thread.IsBackground = false;
thread.Start();
}
public LongRunningTask()
{
}
public LongRunningTask(IntPtr userToken)
{
this.userToken = userToken;
}
public void ExecuteTask()
{
//
// This is executed on the new thread
//
ImpersonateCallingThread();
// ... do work
UndoImpersonation();
}
private void ImpersonateCallingThread()
{
this.impersonationContext =
WindowsIdentity.Impersonate(this.userToken);
}
private void UndoImpersonation()
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
}