C# start process as user from a different domain - c#

I am trying to open some locally installed applications from my home computer but run them using my work Windows account. I need to run them using my work account because they authenticate with Windows Authentication and there are natural restrictions using my local home Windows user.
For example, I can successfully run SSMS using my work account if I run this command:
runas /netonly /noprofile /user:MYWORKDOMAIN\MYWORKUSERNAME "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\ssms.exe"
My home computer is connected via a VPN but otherwise it is not under my work domain. The above command NEEDS the /netonly option to work.
However, I have a number of other apps that need this functionality and I am trying to write a small C# app that can run these apps without needing to enter my password each time. I have tried using the below snippet without success - I get an error saying the username and password are incorrect (they aren't).
var process = new System.Diagnostics.Process
{
StartInfo =
{
Domain = "MYWORKDOMAIN",
FileName = AppPath,
Password = password,
UserName = username,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Normal,
}
};
process.Start();
Can someone tell me what I'm missing please?
EDIT 1 - results for nickpeq's answer
After implementing the code from the provided link, I used it like this:
Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);
using (new Impersonator("MYWORKUSERNAME", "MYWORKDOMAIN", "MYWORKPASSWORD"))
{
Console.WriteLine("During impersonation: " + WindowsIdentity.GetCurrent().Name);
}
Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
And these were the results:
Before impersonation: DESKTOP-WIN11\mark
During impersonation: DESKTOP-WIN11\mark
After impersonation: DESKTOP-WIN11\mark
There was no error thrown (so I guess that's progress 😅)

Check this answer: https://stackoverflow.com/a/11672293/7292986
I see many unanswered StackOverflow threads trying to do the same thing, but it looks like this answer may actually achieve what you are looking for (albeit with significant complexity).

Related

windows cli from c#: how to run as admin user without password?

I'm writing a simple Windows form that runs some commands through cmd.exe in C#. The first code works correctly because I used the process.StartInfo.Verb = "runas" statement.
During execution I agreed to running as administrator without any password.
Now I'm modifying the code because, sometimes, the commands return a choice (like [y/n]) so I want to examine the output and, eventually, send a choice.
To do this, I need to redirect in/out flow (StandardInput/StandardOutput) and set process.StartInfo.UseShellExecute = false. This results in Windows not asking me to open as administrator. To solve this, I used the following:
process.StartInfo.Domain = "DESKTOP-2K....";
process.StartInfo.UserName = "Marco";
SecureString password = new SecureString();
process.StartInfo.Password = password;
I get information with command -> wmic useraccount list full.
Unfortunately, this doesn't work.
The curious things is that, with wmic command, I not read that "Marco" is administrator account but I read that "Administrator" is an administrator account. Therefore, Marco doesn't require a password instead Administrator requires a password.
Other curious thing is that Marco is an administrator account if I go to -> Control Panel -> Accounts -> User Accounts
Please help me.
regards

Using reg.exe in code with limited privileges

I need to get installed software list under restricted user.
I use this code:
string fullString = string.Format("EXPORT \"{0}\\{1}\" \"{2}\" /y", hiveString, keyPath, Path.GetTempFileName());
Log(fullString);
var p = Process.Start(new ProcessStartInfo("reg.exe", fullString) {RedirectStandardOutput = true, UseShellExecute = false,WorkingDirectory = Directory.GetCurrentDirectory()});
Log("Output: " + p.StandardOutput.ReadToEnd());
p.WaitForExit();
On my dev machine I see normal output:
operation completed successfully
No matter what account I use - admin or restricted user.
Then I ran this app on Windows XP under restricted user. And see next in log:
"Output: "
Empty line, yes.
When I run similiar query in cmd - it works fine. I can not understand, what I'm doing wrong.
Why doesn't reg.exe write anything?
You can't call reg.exe without admin privileges. At least not unless you are on Windows Millennium (that's why it doesn't even appear on MSFT Web Site). Imagine what one could do if it were possible...

Preventing registry entry code from requiring admin previliges

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.

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;

Network Authentication when running exe from WMI

I have a C# exe that needs to be run using WMI and access a network share. However, when I access the share I get an UnauthorizedAccessException. If I run the exe directly the share is accessible. I am using the same user account in both cases.
There are two parts to my application, a GUI client that runs on a local PC and a backend process that runs on a remote PC. When the client needs to connect to the backend it first launches the remote process using WMI (code reproduced below). The remote process does a number of things including accessing a network share using Directory.GetDirectories() and reports back to the client.
When the remote process is launched automatically by the client using WMI, it cannot access the network share. However, if I connect to the remote machine using Remote Desktop and manually launch the backend process, access to the network share succeeds.
The user specifed in the WMI call and the user logged in for the Remote Desktop session are the same, so the permissions should be the same, shouldn't they?
I see in the MSDN entry for Directory.Exists() it states "The Exists method does not perform network authentication. If you query an existing network share without being pre-authenticated, the Exists method will return false." I assume this is related? How can I ensure the user is authenticated correctly in a WMI session?
ConnectionOptions opts = new ConnectionOptions();
opts.Username = username;
opts.Password = password;
ManagementPath path = new ManagementPath(string.Format("\\\\{0}\\root\\cimv2:Win32_Process", remoteHost));
ManagementScope scope = new ManagementScope(path, opts);
scope.Connect();
ObjectGetOptions getOpts = new ObjectGetOptions();
using (ManagementClass mngClass = new ManagementClass(scope, path, getOpts))
{
ManagementBaseObject inParams = mngClass.GetMethodParameters("Create");
inParams["CommandLine"] = commandLine;
ManagementBaseObject outParams = mngClass.InvokeMethod("Create", inParams, null);
}
Having followed the link suggested by Isalamon above (thanks) I followed Jestro's advice and have rewritten using psexec.exe (which can be downloaded from http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx) instead of WMI. It feels like a bit of a kludge to do it this way, but it seems to work.
New code for anyone who is experiencing similar problems:
Process proc = new Process();
proc.StartInfo.FileName = "PsExec.exe";
proc.StartInfo.Arguments = string.Format("\\\\{0} -d -u {1}\\{2} -p {3} {4}",
remoteHost,
domain,
username,
password,
commandLine);
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
WMI just uses impersonation when executing the remote process, which does not give you network access. If you are ok going outside managed code, you can just map a UNC path in the remote process WMI started using whatever credentials you want. Then, you have the network access you want. I use NetUseAdd and NetUseDel from netapi32.dll to map the UNC path. See http://pinvoke.net/ for details on the using the APIs.
I know you've sorted it by using PSEXEC, which is a fantastic program, but if you did want to go back to WMI, have you tried enabling the following in your ConnectionOptions:
The flag EnablePrivileges
setting the Impersonation to ImpersonationLevel.Impersonate
Which does the following:
Gets or sets a value indicating whether user privileges need to be
enabled for the connection operation. This property should only be
used when the operation performed requires a certain user privilege to
be enabled (for example, a machine restart).
http://msdn.microsoft.com/en-us/library/system.management.connectionoptions.enableprivileges.aspx
Gets or sets the COM impersonation level to be used for operations in this connection.
http://msdn.microsoft.com/en-us/library/system.management.connectionoptions.impersonation.aspx
I think they should tell your WMI to actually allow the program to have the correct credentials and thus access your network share
You can write all you commands to batch file to the remote machine which includes net use (no need to use a drive letter) to do an authentication. Works fine that way. I am still working on an alternative.

Categories

Resources