Preventing registry entry code from requiring admin previliges - c#

In my application, I am trying to set my application as startup by adding registry entry using the following code:
RegistryKey startupapp = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
startupapp.SetValue("App", "\"" + Assembly.GetExecutingAssembly().Location + "\"" +" -start");
But the problem is the registry is added only when I run my application as an administrator. Is there any way I could avoid this this thing, so that my application could add/delete registry entries for the startup app?

That is because HKLM requires admin rights to write to. HKCU doesn't require that.
RegistryKey startupapp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
startupapp.SetValue("App", "\"" + Assembly.GetExecutingAssembly().Location + "\"" +" -start");

Since you want to set the application to run on startup for all users, no, you cannot get around requiring some kind of administrator access.
Why is this? Well, as a thought experiment, let's say that Windows allowed applications to set programs to run on startup without requiring an administrator password. But this would defeat the purpose of a lot of the security improvements in Vista, with the introduction of UAC and the advent of non-administrator accounts by default. A user could download a game that was actually a Trojan, and the game could set some malware to run on startup for all users, all without requiring the UAC prompt. Therefore, Windows doesn't allow you to set the program to run on startup without administrator rights.
There are some tricks you can use to get around requiring the user to run your whole program as an administrator, though. My first idea is to have a second program whose sole job is to set the main program to run on startup, and to start it with option ProcessStartInfo.Verb = "runas" (assuming that you're using System.Diagnostics.Process to start the new process) so that the helper program runs as an administrator, but your overall application can be started without the UAC prompt.
EDIT: In response to your comment
Yes, if you only care about starting the program on startup for a single user, you have a lot more options. As this O'Reilly article describes, one option is to write to HKCU\Software\Microsoft\Windows\CurrentVersion\Run in order to set your program to run at startup for the current user.

Related

How to make application run as admin on user accounts?

Is it possible to make a .NET application always an admin while being run on a user account without a UAC popup? I've spent some time searching for this capability but haven't found any satisfactory answers.
For some background info, users are running a test application in a manufacturing environment where a dongle is plugged into a USB(to serial) port. Sometimes windows messes up the COM port and cycling the port can resolve the issue. We have discovered we can do this programmatically with admin privileges, but we do not want the users to be admins, and we also don't want the users to deal with a UAC popup or, god forbid, click "no" on the UAC popup to disable our capabilities and mess up the entire process.
How do I force my .NET application to run as administrator?
I have found this old thread but their solutions all require the user to be admin or the usual UAC popup.
Is there something we can do to enable this capability or are we forever chained to the UAC prompt? We do own these machines and control the applications and users running on them.
EDIT: We are cycling the COM port using this method:
string ComPortName { get; set; } = "USB Serial Port (COM12)";
private void button1_Click(object sender, EventArgs e)
{
SelectQuery query = new SelectQuery("Win32_PnPEntity", "Name=" + '"' + ComPortName + '"');
ManagementObjectSearcher myDevices = new ManagementObjectSearcher(query);
foreach (ManagementObject item in myDevices.Get())
{
textBox1.AppendText("Disabling port " + ComPortName + Environment.NewLine);
ManagementBaseObject inParams = item.InvokeMethod("Disable", null, null);
Thread.Sleep(3000);
textBox1.AppendText("Enabling port " + ComPortName + Environment.NewLine);
ManagementBaseObject UWFEnable = item.InvokeMethod("Enable", null, null);
Thread.Sleep(3000);
textBox1.AppendText("Finished cycling port " + ComPortName);
}
}
You can't, not without at least an UAC prompt. This is totally unavoidable, otherwise Windows wouldn't have any security if it was possible - it MUST be totally impossible, not just "difficult", to bypass UAC.
Some clues to solve anyway your problem:
You can force your application to always ask for elevation (i.e. make it to require administrative privileges) since the first step. Dangerous, but at least, you won't mess the whole software chain: it would be elevated from start.
You can ask for UAC only when it's really needed (for example, when launching a particular sub-process, or launching your own application in elevated mode while keeping context). Obviously, you'll ask again and again until the elevated subprocess is created: if user click on "No", then you try again to launch it. Annoying, but again, you won't mess up the whole process.
You can write a Windows service, that will run under administrative privileges, to perform the COM cycle task you need. Then, you can invoke this service from non-elevated user space, without requiring any elevation. I would recommend this solution.
No, you cannot.
Imagine the user is a standard user - e.g. a 5 year old daughter
She cannot just become an administrator.
If anyone could just become an administrator - then there's no point in having security. And Windows NT is a secure operating system.
Solution to your problem
The easiest way to solve your problem is to grant Everyone permission to do the thing. Because in order to do it: someone needs permission.
We have discovered we can do this programmatically with admin privileges
You don't mention what actions you are taking programmatically, or what Windows API you're calling.
If it involves a registry key, or file, or service
Grant Modify permission
to the Users group (or the Everyone group if you prefer)
This way the user's then have permission to do these things.
There's no shame in granting your users permissions to do the thing - it's what you want them to do. They should have permissions to do it.
If you care about defense-in-depth, you could follow the priciple of least privilege, and rather than granting everyone permission to do the thing, you can grant it to one user:
the user that a service runs as
the user that a scheduled task runs as
And then you only need to worry about your program communicating with a service (asking it to do the thing), or trigger the scheduled task (so that it can do the thing).
This way it's only the service/task that has permissions - and you grant that service/task the Modify permission on the thing that your standard users are currently denied from.
Imagine Windows XP
Imagine how you would have solved this before UAC:
the user is a standard user
and there is no UAC convenience feature to help them elevate to an administrator
In that case you would have to do one of the above:
grant everyone permission to the registry key, or folder
create a service or scheduled task that does have permission: and have your program ask them to do it

Writing Run (on startup) registry key

My goal is to have my application run on startup.
My problem is my application is not writing to 'Run' in regedit.
I have this code
RegistryKey rWrite = Registry.CurrentUser.OpenSubKey(#"HKey_Current_User\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
and I believe what this is suppose to do is write my application to
HKey_Current_User\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\run
However, this is not writing anything to regedit.
Things to note:
My application forces the user to run in administrator. It essentially checks if they have ran in administrator and if they haven't it displays a messagebox then closes the program with
Environment.Exit(0);
You got the Key with write access (Probably does not exist because you got CurrentUser inside CurrentUser), but you are not setting any values inside. If you want your program to start, you need to set your application path inside. Here's how you would do it:
var rWrite = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
rWrite.SetValue("YourOwnKeyForYourApp",
AppDomain.CurrentDomain.BaseDirectory + AppDomain.CurrentDomain.FriendlyName);

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...

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

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.

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;

Categories

Resources