I do impersonation of a non-admin user account in an app that is running as an admin user (using LogonUser(), DuplicateToken() and WindowsIdentity.Impersonate() functions). Since this user account is temporary, I also need to load a user profile (using LoadUserProfile() native function). All methods execute successfully (no last error is set) and the current identity is the impesonated non-admin user as expected. However, when I try to run a new process with System.Diagnostics.Process.Start(), I get an error:
Access is denied.
When I try to manually execute the same scenario with runas /profile /user:mynonadmin user, everything works fine.
What am I missing here?
Ran into this a while back.
The impersonated user did not have access to the CWD which was set on the Process object. Create a ProcessStartInfo object and set the working directory to a location the impersonated user has access to.
I had a very similar situation with a service project. Here is some over-simplified pseudo code to give you an idea of what I was doing:
uint ConsoleSessionID = WTSGetActiveConsoleSessionId()
WTSQueryUserToken(ConsoleSessionID, out hToken)
IsUserInAdminGroup(hToken)
DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out IntPtr DuplicateToken)
WindowsIdentity.RunImpersonated(new SafeAccessTokenHandle(DuplicateToken), () =>
{
Process p = new Process();
p.StartInfo.FileName = ...
p.StartInfo.Arguments = ...
p.WorkingDirectory = ...
p.StartInfo.UseShellExecute = true;
p.StartInfo.Verb = "runas"; // Elevated!
p.Start();
}
This worked ABSOLUTELY FINE when logging into Windows using the local Administrator account.
However, if I created a "Test_User" account that was a member of the administrators group, I was getting ACCESS DENIED OR a 0xc0000142 exception from Process.Start().
Allow service to interact with desktop
After checking "Allow service to interact with desktop", now whether I used the actual local Administrator account, or any other account that is a member of the local administrators group, my service can now start an elevated application in the context of the logged-on user.
Of course I went back and updated my code for installing the service, to ensure the SERVICE_INTERACTIVE_PROCESS flag was set, so this option was set programmatically.
Hope this helps someone...
Related
I wrote an application, that grab time from NTP server and change system time of my machine. It works good, but time synchronizing only if my apps start with admin rights. So a question is how to launch it without admin privileges, but to save it functionality?
I set system time using WinAPI function SetSystemTime.
you can do this in several ways (have the most proper one for you).
Identity Impersonation => described with example at this address
WindowsIdentity identity = new WindowsIdentity(accessToken);
WindowsImpersonationContext context = identity.Impersonate() ;
runas verb
ProcessStartInfo startInfo = new ProcessStartInfo (m_strInstallUtil, strExePath);
startInfo.Verb = "runas";
System.Diagnostics.Process.Start (startInfo);
please keep in mind that UAC will be prompted for the second method
PrincipalPermissin (if the user is the member of Admin group)
[PrincipalPermission(SecurityAction.Demand, Role = #"BUILTIN\Administrators")]
I am having an issue with a process being run, where the profile directory of the process' user has not yet been created.
To explain, here are the details of how this is happening:
We run a large distributed server grid, and are using (parts of) DataSynapse to execute processes on this grid. For those familiar with DataSynapse, the Engine is configured to run the particular service with "RunAs", where we use a certain AD domain service account for the service processes. I believe the problem is that DataSynapse, when running the process under "runas", does not set the LoadUserProfile flag (nor should it). Whatever the precise reason, if the "runas" service account (and AD domain account) has never logged on to some of the grid machines, then those machines will not have the user profile directory for the account.
For those not familiar with DataSynapse, here is a more generic explanation. On each machine on the grid, there is a process running, I'll call it dsService, and it runs under the credentials of the local machine's system account (or some similar account with elevated credentials). The process dsService will spawn a child process, say childProcess, but it runs childProcess under the credentials of our AD domain account, which I'll call serviceUser. There are thousands of machines on the grid, and typically they are never logged on to manually. In particular, the profile directory C:\users\serviceUser may not initially exist. Once it is created once, there are not further issues. But if new nodes are added to the grid, typically they will not have the C:\users\serviceUser initially. The problem is that when the dsService spawns childProcess, C:\users\serviceUser does not get created, and we need it.
I believe that this is because dsService does not set the LoadUserProfile flag to true when spawning childProcess, though I am not certain.
In any event, childProcess is a (.net) process (running as serviceUser) under our control, and I would like to know if there is a way (in C#) that childProcess can force the OS to create the running user's profile directory C:\users\serviceUser when it determines that it does not yet exist?
Edit:
Experimentation has confirmed that if one starts a process under another user ID, and the user's profile directory is not there (more specifically, if the user's local profile has not been created yet - merely deleting a pre-existing profile directory creates a different situation, one we're not interested in anyway), then (1) the profile directory (and one presumes, the local profile) gets created if the process is started with the LoadUserProfile set to true; and (2) the profile directory (and one presumes, the local profile) does NOT get created if the process is started with the LoadUserProfile set to false. This makes sense, and is as one would expect.
Related post: stackoverflow.com/q/9008742/1082063
If the running account has admin privileges, the following code will cause the creation of the running account's profile, including its UserProfile directory. Without admin, I don't know if it is possible:
using System.Runtime.InteropServices;
...
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int CreateProfile(
[In] string pszUserSid,
[In] string pszUserName,
System.Text.StringBuilder pszProfilePath,
int cchProfilePath);
....
public static string getUserProfilePath()
{
string userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if(string.IsNullOrWhiteSpace(userProfilePath) || !Directory.Exists(userProfilePath))
{ //This will only work if we have admin...
var pathBuf = new System.Text.StringBuilder(240);
var Up = System.DirectoryServices.AccountManagement.UserPrincipal.Current;
if( 0 == CreateProfile(Up.Sid.ToString(), Up.SamAccountName, pathBuf, pathBuf.Capacity) }
{
userProfilePath = pathBuf.ToString();
}
}
return userProfilePath;
}
If anyone can tell me how to do this when the account is not admin, they will get their answer accepted as the correct answer. Until then, this at least gives others some idea.
I am running an executable process from my ASP.NET application when a user clicks a button. This process creates several files and serves them up to the end-user. I can't really see what the process is or isn't doing, but it didn't work until I specified the admin user as the application pool identity on the server. I am using IIS7.
using (var proc = new Process())
{
proc.StartInfo.FileName = Server.MapPath("~/Testing/Demo/MyExe.exe");
proc.StartInfo.Arguments = String.Format("\"{0}\"", commandFilePath);
proc.StartInfo.UseShellExecute = true;
proc.Start();
proc.WaitForExit();
}
I'm assuming that this is generally a bad thing to do. Can you give me insight into what needs to be done in order to enable this for the normal ApplicationPoolIdentity account?
Thanks!
First of all, why you need the Shell to execute it ? Isn't a console application - do you open any window ?
Second you need to redirect the input and the output.
And final, what you need to do, is to place on the directory that your script runs, permission for the user under witch your pool is run. And remove the Admin from your pool.
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
proc.StandardInput.Flush();
proc.StandardInput.Close();
proc.WaitForExit();
proc.Close();
So for example, if you add your pool to run under the UserA, then go to your directory that your program runs and add permission for the UserA to been able to execute programs on that directory. If your program also use other directories to read and write, also add permission to the UserA for that ones.
I can't really see what the process is or isn't doing
You can take a look if you use on the server the Process Explorer and see if its runs, if its close, if its stop but stay there.
It is likely a file/execution permissions issue.
Try granting execute permissions to the ApplicationPoolIdentity to ~/Testing/Dema/MyExe.exe and read permissions to commandFilePath. You mentioned that your process creates files. You will need to grant either modify or full control permissions to the ApplicationPoolIdentity on the folder where the files will be created. Here is a matrixed list of permissions.
See assign permissions to ApplicationPoolIdentity account for information on granting permissions.
The security event log should capture permission denied errors. Check there to see if you have access permission issues. The System and application logs might also contain information on the problem.
Process Explorer can also show File Access requests. Here is a technet article on troubleshooting with Process Explorer.
Whenever you run any process from an ASP.NET page, it runs under the security context of the worker process, the privilege of your app pool account. It is not like you normally running the MyExe.exe, in that case it will run using logged in account. It is because of this, your code worked when you gave Admin account to app pool.
There are many ways to solve this issue.
One of the easiest would be to change your app pool identity to Network Service and add the Network Service to permissions of the folders in which the MyExe.exe will be accessing files form.
Hope it helps.
Thank you all for your help. All I needed to do was set the StartInfo.WorkingDirectory to somewhere that I was able to write.
using (var proc = new Process())
{
proc.StartInfo.FileName = Server.MapPath("~/Testing/Demo/MyEXE.exe");
proc.StartInfo.Arguments = String.Format("\"{0}\"", commandFile);
proc.StartInfo.WorkingDirectory = savePath;
proc.Start();
proc.WaitForExit();
}
This causes the temp files to be written to a non-system folder and thus does not need any elevated permissions for the application pool.
I am working on a web application that install software on a server.
I can run the install by hand if I log in a user that is apart of a specific group then run install msi as administrator.
This specific group is apart of the local administrators group.
My app pool is running as Network_Service.
Do I impersonate then use the runAs verb? but then I need to know the u/p as user and u/p of Administrator .. I think
I am using the System.Diagnostics.Process classes.
System.Diagnostics.ProcessStartInfo oInfo = new System.Diagnostics.ProcessStartInfo(str);
oInfo.UseShellExecute = false;
oInfo.ErrorDialog = false;
oInfo.CreateNoWindow = false;
oInfo.RedirectStandardOutput = true;
Process p = System.Diagnostics.Process.Start(oInfo);
System.IO.StreamReader oReader2 = p.StandardOutput;
string sRes = oReader2.ReadToEnd();
oReader2.Close();
return sRes;
You need to set the UserName and Password properties to the login credentials of an Administrator account.
You can't change the user context of a running process later on. I suggest u use windows authentication and impersonation to be sure the web request is executed as the authenticated user and besides that you don't have to care about the user credentials.
I'm trying to install a service using InstallUtil.exe but invoked through Process.Start. Here's the code:
ProcessStartInfo startInfo = new ProcessStartInfo (m_strInstallUtil, strExePath);
System.Diagnostics.Process.Start (startInfo);
where m_strInstallUtil is the fully qualified path and exe to "InstallUtil.exe" and strExePath is the fully qualified path/name to my service.
Running the command line syntax from an elevated command prompt works; running from my app (using the above code) does not. I assume I'm dealing with some process elevation issue, so how would I run my process in an elevated state? Do I need to look at ShellExecute for this?
This is all on Windows Vista. I am running the process in the VS2008 debugger elevated to admin privilege.
I also tried setting startInfo.Verb = "runas"; but it didn't seem to solve the problem.
You can indicate the new process should be started with elevated permissions by setting the Verb property of your startInfo object to 'runas', as follows:
startInfo.Verb = "runas";
This will cause Windows to behave as if the process has been started from Explorer with the "Run as Administrator" menu command.
This does mean the UAC prompt will come up and will need to be acknowledged by the user: if this is undesirable (for example because it would happen in the middle of a lengthy process), you'll need to run your entire host process with elevated permissions by Create and Embed an Application Manifest (UAC) to require the 'highestAvailable' execution level: this will cause the UAC prompt to appear as soon as your app is started, and cause all child processes to run with elevated permissions without additional prompting.
Edit: I see you just edited your question to state that "runas" didn't work for you. That's really strange, as it should (and does for me in several production apps). Requiring the parent process to run with elevated rights by embedding the manifest should definitely work, though.
This code puts the above all together and restarts the current wpf app with admin privs:
if (IsAdministrator() == false)
{
// Restart program and run as admin
var exeName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
ProcessStartInfo startInfo = new ProcessStartInfo(exeName);
startInfo.Verb = "runas";
System.Diagnostics.Process.Start(startInfo);
Application.Current.Shutdown();
return;
}
private static bool IsAdministrator()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
// To run as admin, alter exe manifest file after building.
// Or create shortcut with "as admin" checked.
// Or ShellExecute(C# Process.Start) can elevate - use verb "runas".
// Or an elevate vbs script can launch programs as admin.
// (does not work: "runas /user:admin" from cmd-line prompts for admin pass)
Update: The app manifest way is preferred:
Right click project in visual studio, add, new application manifest file, change the file so you have requireAdministrator set as shown in the above.
A problem with the original way: If you put the restart code in app.xaml.cs OnStartup, it still may start the main window briefly even though Shutdown was called. My main window blew up if app.xaml.cs init was not run and in certain race conditions it would do this.
According to the article Chris Corio: Teach Your Apps To Play Nicely With Windows Vista User Account Control, MSDN Magazine, Jan. 2007, only ShellExecute checks the embedded manifest and prompts the user for elevation if needed, while CreateProcess and other APIs don't. Hope it helps.
See also: same article as .chm.
[PrincipalPermission(SecurityAction.Demand, Role = #"BUILTIN\Administrators")]
This will do it without UAC - no need to start a new process. If the running user is member of Admin group as for my case.
i know this is a very old post, but i just wanted to share my solution:
System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo
{
UseShellExecute = true, //<- for elevation
Verb = "runas", //<- for elevation
WorkingDirectory = Environment.CurrentDirectory,
FileName = "EDHM_UI_Patcher.exe",
Arguments = #"\D -FF"
};
System.Diagnostics.Process p = System.Diagnostics.Process.Start(StartInfo);
NOTE: If VisualStudio is already running Elevated then the UAC dialog won't show up, to test it run the exe from the bin folder.
You should use Impersonation to elevate the state.
WindowsIdentity identity = new WindowsIdentity(accessToken);
WindowsImpersonationContext context = identity.Impersonate();
Don't forget to undo the impersonated context when you are done.