I have a winforms app that installs other apps in a loop. This works properly on an administrator account in Windows 7, but I have serious issues in a standard account - the app requires elevation in order to write to "Program Files(x86)" folder.
Therefore I am trying to ask for elevation for a specific method (the one that runs the installers) in a winforms c# app, using this code:
[System.Security.Permissions.PrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Role = #"BUILTIN\Administrators")]
After receiving an error, I learned from the web that before calling the method which carries the above attribute, I need to write this:
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
I did this, and the method still throws the following error:
Request for principal permission failed.
Step by step debugging passes the SetPrincipalPolicy line but, when it reaches the method with the Demand atribute, it just throws the same error, as if the SetPrincipalPolicy never existed.
Am I doing something wrong in setting the Demand attribute properly?
Thank you in advance.
LATER EDIT: as requested here is the code that is supposed to trigger the elevation request when installing the app silently (but does not work):
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
ProcessStartInfo psi = new ProcessStartInfo(file);
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = true;
psi.Verb = "runas";
//psi.CreateNoWindow = true;
psi.Arguments = modifiers;
try
{
using (Process process = Process.Start(psi))
{
process.WaitForExit();
if (process.HasExited)
return process.ExitCode;
}
}
catch (Win32Exception wex)
{
}
}
What I need, is for that process to pop a dialog asking for username and password for admin, if the app was ran under a Windows Standard User. Only the process started programmatically above should run as admin, the main app itself can remain as a standard user.
This is just not the way UAC works. It is process based, the user only ever gets the "please let me mess with your machine" prompt when you start a new process. With the proper incantation of "I need the user's consent to mess with the machine, please say Yes" signal embedded in the program. Which you do by this answer.
Death to the idea of making it method based. Unreasonable to a programmer, makes sense to a user. User wins.
You can either force your app to always run as an admin. This is how you do that. It is not recommended however for your app to need admin privileges to run.
If you start a Process to run the installer, you can check here how to run the process as an admin.
A third option which Visual Studio uses is that when you do something where you need admin privileges you are prompted to restart the app and it then restarts the app as an admin and you can perform the tasks. Just use the code from the second way to start your app.
The method you've posted to run as admin will check if the user is admin and then start the process as an admin. If the user doesn't have admin rights the app won't even start. A better solution is to always try to run the process as an admin. Then the user will get an UAC prompt with password and username, which an admin can fill in.
public static int RunAsAdmin(string fileName)
{
ProcessStartInfo psi = new ProcessStartInfo(fileName);
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = true;
psi.Verb = "runas";
psi.Arguments = modifiers;
try
{
using (Process process = Process.Start(psi))
{
process.WaitForExit();
if (process.HasExited)
return process.ExitCode;
}
}
catch (Win32Exception wex)
{
}
return 0;
}
Related
I trying to make a program which connect to a game ("quick connect").
When i start the application as an administrator and connect to the game, it have weird issues (e.g. shortkeys doesn't respond for the ingame keys and the game doesn't load some model files), but when i start without admin rights it works fine, however i need the admin rights for other purposes (file operations).
I tried to rerun the program with the runas commandline command, the app had no admin rights, but the game still doesn't work right.
// OnClick event
ProcessStartInfo proc = new ProcessStartInfo("cmd.exe", "/c runas /trustlevel:0x20000 \"myapp.exe rerun\"");
proc.WorkingDirectory = Application.StartupPath;
proc.CreateNoWindow = true;
proc.UseShellExecute = false;
Process.Start(proc);
// Program.cs
if(!admin && args[0].Equals("rerun"))
{
ProcessStartInfo proc = new ProcessStartInfo("cmd.exe", "/c game.exe ipAndPortToConnect");
proc.WorkingDirectory = Application.StartupPath;
proc.CreateNoWindow = true;
proc.UseShellExecute = false;
Process.Start(proc);
}
I tried many combinations but none of them worked.
I can't figure out what the hell causing it...
If you check the Task Manager, you'll see the game still runs as a different user, namely Administrator. This most likely means the game saves some information in the user profile, for example under AppSettings (like hotkeys in configuration files), or game modifications that are stored in AppData, which could cause the issues you mentioned.
The user who 'reruns' your program is still the elevated user, albeit with less permissions, and that user launches the game. You cannot "unimpersonate" a process properly to the point the OS thinks it runs as the logged in user, so you'll have to do something else.
You can either see if you can move the user-specific resources to a path relative to the game, not the user, or start the process by any other means but directly from yours, for example using a Scheduled Task.
But why didn't you update your existing question?
I wrote this code to open my application - the name of the executable is C# code analyser.exe. When I start it under Windows 7 (I don't know how this behaves under different versions of Windows), it displays the following message.
Do you want to allow to following program to make changes to this computer?
So I want Windows to not display it to me! What must I do to prevent this message from displaing?
System.Diagnostics.Process Process = new System.Diagnostics.Process();
Process.StartInfo.FileName = (System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "C# code analyser.exe"));
Process.StartInfo.WorkingDirectory = (System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "C# code analyser.exe"));
Process.Start();
Use this instead of your code
System.Diagnostics.Process oProcess = new System.Diagnostics.Process();
oProcess.StartInfo.FileName = "HelloWorld.exe";
oProcess.Start();
or you can pass administrator username & password this way
Process.Start(path + "HelloWorld.exe", uname, password, domain);
This analyzer project, most probably, have a manifest that request administration mode to run. This means that it will keep raising UAC if the starter process(your app) is not elevated.
You can try run you application as an administrator (right click-run as admin) and then the analyzer will inherit the elevation and it will not raise the UAC message.
This following code is pretty simple and it works in a Console Application. But for some reason it does not work in a WCF Service. The directory which has the batch file has full permissions. Can someone help me? What am I missing?
try
{
ProcessStartInfo psi = new ProcessStartInfo();
//specify the name and the arguements you want to pass
psi.FileName = ConfigurationManager.AppSettings["BatchFileLocation"];
psi.Arguments = filePath;
//Create new process and set the starting information
Process p = new Process();
p.StartInfo = psi;
//Set this so that you can tell when the process has completed
p.EnableRaisingEvents = true;
p.Start();
//wait until the process has completed
while (!p.HasExited)
{
System.Threading.Thread.Sleep(1000);
}
//check to see what the exit code was
if (p.ExitCode != 0)
{
logger.Write(p.ExitCode);
}
}
catch (Exception ex)
{
logger.Write(ex.Message);
}
I assume this an IIS Hosted WCF Service.
Check the Identity associated with your application pool.
In IIS 7, and 6.0 to the best of my knowledge, this is the NetworkService, which may or may not have rights to the Batch file, SFTP, etc. In 7.5 there is another account: Default Application Pool ID Changed in IIS 7.5. It is also possible that your pool is using some other account, including a machine specific one, and not a domain account.
We have an internal page that I want to use to run an executable that updates some files on the server. In other words, rather than logging in to the server every time I need to manually run this executable, I would like to run it from the browser. The executable is self-contained on the server and does not interact with the user.
Here is my code:
try
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = Server.MapPath(#"\iPhoneXMLCreator.exe");
p.StartInfo.WorkingDirectory = Server.MapPath(#"\");
p.StartInfo.RedirectStandardOutput = false;
p.Start();
p.WaitForExit();
lblResult.Text = "Success!";
}
catch (Exception ex)
{
lblResult.Text = "Oops, there was a problem.<br><Br>" + ex.Message;
}
When I run it, the process shows up in Task Manager, but then exits within a few seconds without updating the files it is supposed to. There are no arguments to be passed, just a simple executable. Any ideas?
I would start by checking to see if the account which runs the web application has the appropriate permissions.
Most likely this is a permissions issue. Since it's the Asp.Net runtime that is executing this program, you need to ensure that the user account that the Asp.Net runtime uses has access to this executable, and to modify any resources (files, databases, etc) that get modified by the executable.
You can do this via impersonation, or by granting rights tot he appropriate accounts. The proper approach is to use impersonation.
http://msdn.microsoft.com/en-us/library/xh507fc5.aspx
Does the executable file run and process the XML when you run it manually on the server logged in as yourself?
Then it may be a simple permissions issue, since unless you are impersonating ...it's probably trying to run the exe under the ASPNET machine account, which most likely doesn't have rights to the folder the XML is in. Just a thought based on the info you provided.
2 things that you could do:
Run Process Monitor while you attempt to run the exe. I've used it many times to help to find security config problems (especially on web servers). It will log every io and registry access, and more importantly indicate success or failure. Get it here: http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx It requires no setup. Great tool!
Redirect stdout on your console exe. This will allow you to capture any error message that it is attempting to write to the console. Here's how to do it:
try
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = Server.MapPath(#"\iPhoneXMLCreator.exe");
p.StartInfo.WorkingDirectory = Server.MapPath(#"\");
// redirect stdout
p.StartInfo.RedirectStandardOutput = true;
var ConsoleOutput = new StringBuilder();
p.OutputDataReceived += (s, e) => ConsoleOutput.AppendLine(e.Data);
p.Start();
p.BeginOutputReadLine(); // if I remember correctly, you have to call Start() first or you get an exception
p.WaitForExit();
string output = ConsoleOutput.ToString();
lblResult.Text = "Success!";
}
catch (Exception ex)
{
lblResult.Text = "Oops, there was a problem." + ex.Message;
}
Rather than playing with website permissions for the Exe, one workaround that uses a level of indirection and puts a buffer between your web site and the Exe is to simply set a flag value into a text file on the web server when the Page representing the Exe is hit.
Set up a scheduled job on the server to check for that flag value every X hours, or minutes, or whenever and if the flag is seen, run the executable. Reset the flag/file when done. This opens up the possibility to check the flag via a webservice or other mechanisms, such that the target Exe doesn't even need to be on the same web server machine.
This is only viable if the exe does not need to run immediately when the page is hit.
Ok, figured it out. It was a data access issue. The .config file for the .exe had an invalid database connection string. Why it would work when logged in, I'm not sure, but it works now.
I have a program that needs to run as a normal user most of the time, but once in a while I need to stop and start a service. How do I go about making a program that runs as a normal user most of the time but elevates into administrator mode for some function?
You can't elevate a process once its running but you could either :-
Restart the process as elevated
private void elevateCurrentProcess()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch
{
// User didn't allow UAC
return;
}
Application.Exit();
}
This method means that your process continues to run elevated and no more UAC promopts - both a good and a bad thing, depends upon your audience.
Put the code that requires elevation into a seperate exe
Set the manifest as requireAdministrator and start it as a separate process. See this sample code
This method means a UAC prompt every time you run the operation.
Best method depends upon your audience (admin types or not) and frequency of the elevated operation.
As far as I know, you need to start a seperate process that runs as the administrator. You can't elevate a process once it's already been started.
See this question.
You need to use what is referred to as Impersonation..
[http://support.microsoft.com/kb/306158][1]
The above shows how it would be accomplished for an ASP.Net app, but the code is probably near identical for your needs.