Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel.ForEach() changes Impersonation Context

Today we deployed our newly created ASP.NET application to the server and soon we realized there was a strange security-related issue which was causing the application to crash. This is an internal application and we use Impersonation to manage how the users access resources. The application however, throws an "Access Denied" exception when the user attemps to access a folder over which they have full control.

The exception was in fact an AggregateException and was being thrown in a method which uses Parallel.ForEach to enumerate over a list and inside the body, it attempts to access the folder, but at this point the Impersonation Context gets changed and the worker thread runs as the application pool's identity, which doesn't have access to the folder hence the exception.

To confirm this, I looked at the process identity before and inside the body of Parallel.ForEach:

string before = WindowsIdentity.GetCurrent().Name;
Debug.WriteLine("Before Loop: {0}", before);

Parallel.ForEach(myList, currentItem =>
{
    string inside = WindowsIdentity.GetCurrent().Name;
    Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId);
});

When I run the app, this is what gets printed out:

Before Loop: MyDomain\ImpersonatedUser

Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 8)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)
Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 9)
Inside Loop: NT AUTHORITY\SYSTEM (Worker Thread 10)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 6)
Inside Loop: MyDomain\ImpersonatedUser (Worker Thread 7)

As you can see, some threads are running as the impersonated identity and some as the application pool (in this case, LocalSystem) and there doesn't seem to be a pattern. The previous frame in the Call Stack window also goes to the unmanaged kernel32.dll, which makes me think CLR isn't validating the context before delegating it to the OS.

Any idea why this is happening? Is that a known issue/bug?

like image 286
PoweredByOrange Avatar asked Sep 26 '14 17:09

PoweredByOrange


People also ask

What does parallel ForEach do?

The Parallel. ForEach method splits the work to be done into multiple tasks, one for each item in the collection. Parallel. ForEach is like the foreach loop in C#, except the foreach loop runs on a single thread and processing take place sequentially, while the Parallel.

Is parallel ForEach blocking?

No, it doesn't block and returns control immediately. The items to run in parallel are done on background threads.

Is parallel ForEach good?

In many cases, Parallel. For and Parallel. ForEach can provide significant performance improvements over ordinary sequential loops. However, the work of parallelizing the loop introduces complexity that can lead to problems that, in sequential code, are not as common or are not encountered at all.

Which is faster parallel ForEach or ForEach?

The execution of Parallel. Foreach is faster than normal ForEach.


1 Answers

Unlike the Task class, Parallel doesn't seem to be capturing the ExecutionContext you're currently running on (which in turns captures the SecurityContext which holds the WindowsIdentity). It uses the one avaliable inside the current Thread.

You have to explicitly capture the desired context:

IntPtr token = WindowsIdentity.GetCurrent().Token;

Parallel.ForEach(myList, currentItem =>
{
   using (WindowsIdentity.Impersonate(token))
   {
      string inside = WindowsIdentity.GetCurrent().Name;
      Debug.WriteLine("Inside Loop: {0} (Worker Thread {1})", inside, Thread.CurrentThread.ManagedThreadId);
   }
});
like image 100
Yuval Itzchakov Avatar answered Oct 10 '22 20:10

Yuval Itzchakov