How to run a non-admin process from admin c# application - c#

I have a c# application that needs to do some things as an admin (some installation stuff) and then it needs to run another process as a non-admin. I haven't done anything with UAC before, but I assume there must be a way to do this, right?
This also needs to be automated, so assume that the c# app is started with admin credentials.
Basically the program will need to do something like this:
// MUST run this process as admin
Process adminInstall = new Process();
adminInstall.StartInfo.FileName = "install.bat";
adminInstall.Start();
adminInstall.WaitForExit();
// CANNOT run this process as admin
Process nonAdminProcess = new Process();
nonAdminProcess.StartInfo.FileName = "runner.cmd";
nonAdminProcess.StartInfo.UseShellExecute = false;
nonAdminProcess.StartInfo.RedirectStandardOutput = true;
nonAdminProcess.OutputDataReceived += new DataReceivedEventHandler(myHandler);
nonAdminProcess.Start();
nonAdminProcess.BeginOutputReadLine();
nonAdminProcess.WaitForExit();

You can embed a manifest in the executable using MT.exe (manifest tool) in the platform SDK after the binary is compiled, but before it is signed. You also have the option of using a custom manifest within your project properties. Open the project properties, then go to the application tab, then change the manifest option from default manifest, to custom manifest. Visual Studio will add a manifest to your project where you can specify "requireAdministrator" privleges. When your app runs, it will provide a UAC prompt, or ask for credentials if logged on as user. There is a good chance sub process will start as admin as well. Otherwise you will need to launch them with the "runas" verb which is undocumented.

Process.Start parameters has one for Username. See Process.Start reference:
http://msdn.microsoft.com/en-us/library/sxf2saat.aspx

There does not appear to be a nice way of doing this using the .Net classes. However, Process.Start with different credentials with UAC on explains a way of doing it using CreateProcessAsUserW after stealing a handle from another process.

If the aforementioned methods aren't viable, then you can try my method. But its ugly; you need to get a handle to a non admin process, then use DuplicateTokenEx (p/invoke) to copy its (non admin) privileges, then pass that into CreateProcessAsUser. You first need to identify a non admin process though, there may not be any. The newly created process will be spawned with whatever privileges the token you copied contained, not the token of the parent process.

Related

Can I call Windows 10 Wifi settings screen without explorer.exe shell?

I have a custom application running as a the shell (Windows 10 Enterprise) for a particular user - i.e. the user boots straight into this application.
However, I want to be able to provide access to the WiFi settings form. I have read that the way to do this is something like
Process.Start("ms-settings:network-wifi");
or
Process.Start("ms-availablenetworks:");
However, as far as I can tell, that relies on explorer running as the shell.
I've tried...
Process proc = new Process();
proc.StartInfo.FileName = #"c:\windows\explorer.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "ms-availablenetworks:";
proc.Start();
All of the above work fine if I run in a normal environment, i.e. with explorer as the shell.
But for this user (with my custom shell application), I get an instance of explorer.exe running and displaying an error, Class not registered
I have also come across using LaunchUriAsync() but I don't think that would help me here, besides it's only available for Windows Store applications for what I've read, which this is not.
Well I managed to get this working
First start explorer on its own, then a second Process.Start() to run the settings page.
Unfortunately, when explorer.exe runs, it displays the taskbar which I don't want. (I had previously assumed I'd be able to hide it with a group policy setting or something but this doesn't appear to be the case).
But I suppose that's another question...

install as administrator but run as current user

my installer run as administrator, but on complete i want the exe to run as current user.
i am using nsis and already tried UAC
!insertmacro UAC_AsUser_ExecShell "" "some.exe" "" "" ""
but still it run as administrator.
tried to use task scheduler
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
td.Actions.Add(new ExecAction("" + path + "", "", null));
td.Settings.DisallowStartIfOnBatteries = false;
td.Settings.AllowHardTerminate = false;
td.Settings.StopIfGoingOnBatteries = false;
td.Settings.ExecutionTimeLimit = System.TimeSpan.Zero;
td.Settings.IdleSettings.StopOnIdleEnd = false;
// Register the task in the root folder
ts.RootFolder.RegisterTaskDefinition("task", td);
but the task also goto administrator and i cant get it to current user.
any ideas ?
My personal recommendation is that you just remove the option to run your application at the end of your installer. The user can just start it from the start menu, it should be highlighted as new and everything.
As you probably know, UAC really changed how common it is for applications to run as a different user than the "logged in" user. You just have to deal with the fact that UAC exists and decide if you feel it is worth the amount of time required to work around it and possible bugs and issues that might arise.
There are at least 4 ways to run something as the "current user", all of them have issues and can fail or end up running as the "wrong" non-admin user:
Use the token of the (hopefully) non-elevated parent, this is what the NSIS UAC plugin does.
Use the Windows Task Scheduler. This was a recommended practice in the early Vista days but I believe Microsoft has moved away from this method.
Use a shell COM object in the Explorer process that hosts the taskbar to call ShellExecute for you. The StdUtils plugin provides a ExecShellAsUser method that does this.
Use a Windows NT service. Because it runs as SYSTEM it can get the token handle of a user in any session.
If you decide that you still want to attempt to do this then you need to decide on your definition of current user before you choose a method.
Is it the user that logged in on the welcome screen? Is it the user the Explorer shell (Taskbar etc) is running as? Is it the parent process of your setup process? You should also keep in mind that runas.exe exists and a user might try to run something as a particular user for a reason...

downgrade rights of a started process

I need to start an application from another application. It looks like I have to use the shell to do it (since I need to be able to close the launcher), but I also would like to downgrade the rights given to the launching application.
Is this possible? The launcher must run as administrator, but I'd like to have the launching application run as user.
this is how I currently run the process:
Process process = new Process();
process.StartInfo = new ProcessStartInfo();
process.StartInfo.FileName = name;
process.Start();
Forgive me I forgot to add a couple of details:
I need to run it in .net 3.5 on mono
I'd prefer to not use native code
I need to run the launcher application in admin mode
This seems to be have discussed before, check this out: How do you de-elevate privileges for a child process
Looks like an UAC elevation is strictly one-way, so the solutions are a bit gnarly, i.e. code injection into explorer and stuff like that.
http://www.codeproject.com/Articles/18946/High-elevation-can-be-bad-for-your-application-How
Eventually I decided to create a bootstrapper that could run the launcher as administrator, but then the application as normal user. Once the launcher is done it goes back to the bootstrap executable which launches the application.

Get current user name when starting with UAC

Our setup has an embedded manifest that triggers the UAC before the application starts. (The applications runs as an admin user). However, if the setup needs to install the .NET Framework, we have to continue the setup after a reboot. For this reason, we have to create a registry key in the current user's RunOnce.
Unfortunatly, HKEY_CURRENT_USER points to the Administrator's registry. We need to find out the user that is currently logged in and started the installation. (Th normal USER clicked the setup.exe, an ADMIN entered his details in the UAC prompt. We need to find out who the USER was)
I've tried all the usual methods (Environment.UserName, WindowsIdentity.GetCurrent())
Thanks!
You can use the LsaEnumerateLogonSessions function to retreive what you need. However, it is a winapi C function call. If you need a managed version of it, I belive you can look at the source code for Cassia, which uses this function in its terminal services API. The call should be the same. You can also look here.
Also you can use the NetWkstaUserEnum WINAPI function. You can find a managed wrapper for it here
With Cassia library this code works fine:
ITerminalServicesManager manager = new TerminalServicesManager();
ITerminalServicesSession session = manager.CurrentSession;
string userInfo = session.DomainName + "\\" + session.UserName;
NTAccount account = session.UserAccount;
Run your initial setup.exe as a small executable that puts up a splash screen while invoking your real setup program as a child process. The small EXE is not run as admin and can pass the logged in user name to the child process. The child process invokes UAC and runs in the admin context but already has the logged in username as a command line parameter.
It is not possible to retrieve the original user if your application is ran as Administrator:
If a user launches Setup by right-clicking its EXE file and selecting
"Run as administrator", then this flag, unfortunately, will have no
effect, because Setup has no opportunity to run any code with the
original user credentials. The same is true if Setup is launched from
an already-elevated process. Note, however, that this is not an Inno
Setup-specific limitation; Windows Installer-based installers cannot
return to the original user credentials either in such cases.
Source : InnoSetup Help
As said by Matthew in comments, you should not run your application as Administrator but only trigger UAC when needed in your code.
This returns the name of the logged in Windows User by stripping out the domain:
using System.Security.Principal; // here is the security namespace you need
...
string userName = WindowsIdentity.GetCurrent().Name.Replace("\\", "|");
string[] split = userName.Split(new Char[] { '|' });
lblDebug.Text = (split.Count() > 1) ? split[1] : userName;

Write-access for c# app in its own exe dir in Windows 7

I know that user accounts in Windows 7 are limited by default, so a program cannot just write anywhere on the system (as it was possible in Win XP).
But I thought that it would be possible that e.g. a c# app is allowed to write inside it's own exe-directory or it's subfolders at least (not everything is 'user settings' or should be written to "MyDocuments"...).
So currently my c# app throws an UnauthorizedAccessException when trying to write inside the exe dir.
Is there anything you can do in c# code to allow writing inside the exe dir?
No, if the user your application is running under doesn't have permissions to write to this folder you cannot write to it. When installing your application (probably through an MSI) you could grant the necessary rights.
You could also provide a manifest file with your application.
Is there anything you can do in c# code to allow writing inside the exe dir?
Yes, but this code (that changes the permissions) would need to be executed with admin permission, so you're back at the start.
In my opinion, the correct way would be to set up appropriate write permissions to a directory below C:\ProgramData (actually: Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)) in a custom action during the installation of your software.
Yes.
Install the program in a location where user's have write/modify rights.
(Of course this opens you to those same users modifying your program.)
Yes, you should make sure you're program runs with admin privileges.
You can do this manually by rightclicking on the exec and click "Run as Administrator" or you can demand from code that the program runs with admin privileges.
WindowsPrincipal pricipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = pricipal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
RunElevated(Application.ExecutablePath);
Environment.Exit(0);
}
private static void RunElevated(string fileName)
{
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = fileName;
try
{
Process.Start(processInfo);
}
catch (Win32Exception)
{
MessageBox.Show("Program needs administrator rights");
}
}

Categories

Resources