Tuesday, September 27, 2011

User Impersonation in .NET

Recently, I've been working on a tool to manage deployments to a web farm. As part of this task, I had to modify our current tool to impersonate a shared login to perform the actions on the various servers. This was so the IT group would not have to grant access to a large number of users. This can be done in .NET, but the approach you have to take to impersonate another user is different depending on the action you are taking. While updating the program, I encountered three scenarios which had to be addressed in different ways.

Scenario #1: Executing .NET code in the current Thread
Scenario #2: Spawning a new Process in .NET
Scenario #3: Making a WMI request

Scenario #1: Executing .NET code in the current Thread
Executing .NET code as a different user may be necessary when performing disk operations, network operations, manipulating services, or other tasks that require security permissions. To start the Impersonation, the WindowsIdentity.Impersonate() method needs to be called with an authorization token for the impersonated user. This entire process requires making an external call to the Windows API to authenticate the user. While it sounds difficult, the MSDN page for the Impersonate() method provides a working sample that can be used as a basis for your own code.

Scenario #2: Spawning a new Process in .NET
Creating a new process already requires setting up a Process object or a ProcessStartInfo object. To set this object to execute as a different user requires just a bit of extra work to pass in a username and password. The following code snippet shows how to create a ProcessStartInfo object to start the command prompt as an Impersonated user.



String userName;
SecureString password;

// Code to get and set userName and password.

ProcessStartInfo startInfo = new ProcessStartInfo(“cmd.exe”);
startInfo.UserName = userName;
startInfo.Password = password;
// Add other parameters to startInfo as needed
Process.Start(startInfo);


Scenario #3: Making a WMI request
A WMI request queries the current or a remote server for system information to monitor the system. This functionality resides in the System.Management namespace, and isn't a common one to work with. WMI does allow for the queries to be ran as another user by creating a ConnectionOptions object, as the below example outlines.



String userName;
SecureString password;

// Code to get and set userName and password.

ConnectionOptions connectOptions = new ConnectionOptions();
options.UserName = userName;
options.Password = password;

ManagementPath path = new ManagementPath(“path to wmi object to query”);
ManagementScope scope = new ManagementScope(path, connectOptions);

// Once we have a scope object, we can either create an ObjectQuery object and query that way
scope.Connect();

ObjectQuery query = new ObjectQuery(“query to execute”);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection collection = searcher.Get();

// Or we can create an ObjectGetOptions and a ManagementObject
ObjectGetOptions options = new ObjectGetOptions();
ManagementObject mgmtObject = new ManagementObject(scope, path, options);
mgmtObject.Get();
// Can now retrieve values from ManagementObject like a Dictionary
string value = mgmtObject[“key name”].ToString();

No comments:

Post a Comment