Yes, there are many articles related to elevating permissions when installing MSI packages. I have a twist on the issue that I can't find a good answer to. If I'm logged in as a user and I run my MSI elevation code (Below), the package installs but the current user actions are performed on the user I elevated the installer with.
For example, if the MSI adds a file to the CURRENT USER's desktop. The result of elevation (running as "Joe Admin") is the file gets put on Joe Admin's desktop -not the currently logged in user ("Sally User"). I have owned software that Elevates as Joe but puts the file on Sally's desktop as if she installed it. -I'd like to write my own. This is on a Windows 7 Machine, UAC is turned off.
Here is the non-working code. (Sally is logged in, Elevate as Joe -File goes to Joe's Desktop) (LoadUserProfile property was an attempt to solve this issue -didn't work).
Process watchThis = ImpersonateInstaller(#"c:\temp\Test.msi", "SuperJoePassword");
watchThis.WaitForExit();
private static Process ImpersonateInstaller(string msiPath, string Password)
{
Domain d = Domain.GetCurrentDomain();
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.LoadUserProfile = true;
process.StartInfo.FileName = #"C:\Windows\System32\msiexec.exe";
process.StartInfo.Arguments = string.Format(#"/i {0} REBOOT=ReallySuppress /qb-", msiPath);
process.StartInfo.WorkingDirectory = Environment.GetEnvironmentVariable("WINDIR");
process.StartInfo.UserName = "JoeAdmin";
process.StartInfo.Password = new SecureString();
process.StartInfo.Domain = d.ToString();
foreach (char c in Password.ToCharArray())
{
process.StartInfo.Password.AppendChar(c);
}
process.Start();
return process;
}
From an elevated process call msiexec /jm foo.msi to perform an advertisement. This blesses the package. From a standard user process call msiexec /I foo.msi REBOOT=R /qb and this will start the installation off as the user but elevate seamlessly as needed. Standard Actions and Custom Actions with No Impersonation will run as SYSTEM and Custom Actions with Impeornation will run as the user without privs as designed.
With Help from Christopher Painter, this appears to be the answer (THANKS CHRISTOPHER!!!)
I've read the words "advertise" before and always assumed it had something to do with 'publishing in GPO' so I never follwoed through. Seems I'm wrong. Here's the trick should anyone else run across this.
First, advertise with elevated rights to "bless" the msi for end user installation. In my mind an adminstrator is saying, sure this msi is safe for Sally end user to install:
msiexec.exe /jm install.msi
Then, install as the end user as if they are admin:
msiexec.exe /i install.msi /your /typcial /installOption /switches /here
My code (surely could be better):
Process advertise = advertiseMSI(#"c:\temp\test.msi", "JoeWhoHasAdminRights", "Joe'sSuperPassword");
advertise.WaitForExit();
Process install = installMSI(#"c:\temp\test.msi");
install.WaitForExit();
private static Process advertiseMSI(string msiPath, string userName, string Password)
{
Domain domain = Domain.GetCurrentDomain();
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = #"C:\Windows\System32\msiexec.exe";
process.StartInfo.Arguments = string.Format(#"/jm {0}", msiPath);
process.StartInfo.WorkingDirectory = Environment.GetEnvironmentVariable("WINDIR");
process.StartInfo.UserName = userName;
process.StartInfo.Password = new SecureString();
foreach (char c in Password.ToCharArray())
{
process.StartInfo.Password.AppendChar(c);
}
process.StartInfo.Domain = domain.ToString();
process.Start();
return process;
}
private static Process installMSI(string msiPath)
{
Process process = new Process();
process.StartInfo.FileName = #"C:\Windows\System32\msiexec.exe";
process.StartInfo.Arguments = string.Format(#"/i {0} REBOOT=ReallySuppress /qb-", msiPath);
process.StartInfo.WorkingDirectory = Environment.GetEnvironmentVariable("WINDIR");
process.Start();
return process;
}
Related
I have a WebAPI that works great but need to add the ability to call to an EXE on the server to run some tasks with Video, the server is our machine and running IIS to host the WebAPI.
I have tried it working with Process() and the calls make it to the cmd.exe file I have written but the issue is that the user is IUSER and this won't work as the Video processing needs to use system hardware so needs to be the current logged in Windows User.
I don't want to give the IUSER this privilege for obvious security reasons so I am looking for another way to call and pass data to the an EXE or background task (Short running <3seconds) and for that process to reply with the results.
It's all on the server which we have full control over.
Current Code:
string exeLoc = Environment.ExpandEnvironmentVariables(baseEXELocation);
using var process = new Process();
process.StartInfo.FileName = exeLoc;
process.StartInfo.Arguments = $"{command}";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, data) =>
{
if (!String.IsNullOrEmpty(data.Data))
{
Console.WriteLine(data.Data);
consoleData += data.Data;
}
};
process.Start();
process.BeginOutputReadLine();
var exited = process.WaitForExit(1000 * 10); // (optional) wait up to 10 seconds
Thanks
You could try to use the impersonation in iis:
<identity impersonate="true" />
https://support.microsoft.com/en-us/help/306158/how-to-implement-impersonation-in-an-asp-net-application
or assign the administrator permission to the application pool by using application pool advance setting -> identity to the custom account.
or You can try using the verb runas in Process.Start to execute the exe file as an Administrator.
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Normal;
proc.FileName = myExePath;
proc.CreateNoWindow = false;
proc.UseShellExecute = false;
proc.Verb = "runas"; //this is how you pass this verb
Were using octopus for deployment, the tentacle is running as "local system account" I would like the tentacle to add credentials for a diffrent account. However I have no luck i doing so.
So far i tried creating a c# program which starts a new process as the other user, and the calls the cmdkey.exe
private static void CallCmdKey(string runAsDomain, string runsAsUser, string runAsPass, string target, string user, string pass)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.Arguments = $"/generic:{target} /user:{user} /pass:{pass}";
proc.StartInfo.FileName = Environment.GetEnvironmentVariable("WINDIR") + "\\system32\\cmdkey.exe";
Console.Out.WriteLine(proc.StartInfo.Arguments);
proc.StartInfo.Domain = runAsDomain;
proc.StartInfo.UserName = runsAsUser;
proc.StartInfo.LoadUserProfile = true;
SecureString sec = new SecureString();
runAsPass.ToCharArray().ToList().ForEach(sec.AppendChar);
proc.StartInfo.Password = sec;
proc.StartInfo.WorkingDirectory = ".";
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
Console.Out.WriteLine("done");
}
But it fails with access denied.
Then i tried power shell and psexec like this:
$psexec = "C:\temp\psexec.exe"
Invoke-Command -ScriptBlock{&$psexec -accepteula -u $WEB02AP2User -p $GISWEB02AP2Pass cmd /c cmdkey /generic:ffff /user:mufasa /pass:yoyo}
but it fails with
Access is denied.
PsExec could not start cmd:
The remote script failed with exit code 5
For security reasons Im not allowed to change account for the tentacle service
How can i sovle this issue
I Was unable to find a solutions to this issue. Only workaround was to let the octopusservice run as a specific user account
I want to start another program which runs as user from a program running as administrator.
The problem is that the second program needs to use outlook, which is not possible if the program runs as admin. The main program needs to run as admin.
I did already come up with this two solutions:
Process.Start("cmd.exe", #"/C runas.exe /savecred /user:" + Environment.UserDomainName + "\\" + Environment.UserName + " " + "\"SomeProgram.exe" + "\"");
or
Process.Start("explorer.exe", "SomeProgram.exe");
But i have a problem with both solutions.
The first one asks the user for the password (only the first time after windows was restarted).
The second one probalby won`t work in the future, because as far as i found out it is considered as a bug and probably fixed with an future update.
So I would like to know is there any other solution, where the user does not need to enter his password?
This seems to work for me:
Process.Start("cmd.exe", #"/C runas.exe /TrustLevel:0x20000 " + "\"SomeProgram.exe" + "\"");
Process class has StartInfo property that is an instance of ProcessStartInfo class. This class exposes UserName, Domain and Password members to specify the user you want to run the process.
Process myProcess = new Process();
myProcess.StartInfo.FileName = fileName;
myProcess.StartInfo.UserName = userName;
myProcess.StartInfo.Domain = domain;
myProcess.StartInfo.Password = password;
myProcess.Start();
I was having the same issue and was not able to get the current logged user. NB: querying wmi is not a solution as many users may be logged in at that time
so my solution is to do the reverse. Launch my app as current user and if the current user is not admin, I request to run as admin.
if (IsAdministrator())
{
// run whatever you want as elevated user
}
else
{
//launch the same app as admin
ExecuteAsAdmin(PATHH_TO_THE_SAME_APP.EXE);
//execute whatever you want as current user.
}
public static void ExecuteAsAdmin(string fileName)
{
Process proc = new Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";
proc.Start();
proc.WaitForExit();
}
public static bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
I'm writing a tool that can be used to manage the virtual host a web server on Ubuntu. As for many of the features I need privileged rights, I look for ways to achieve this.
Currently I realize that with "gksu" or "gksudo". This also works. The problem is that the user is prompted for any activity that requires privileged rights to enter his password.
Is it possible to retrieve this password only once and remember for the rest of the duration of the program? Is there another way to implement this?
public void OnToogledVirtualHost(object o, ToggledArgs args)
{
VirtualHost host = (VirtualHost)store.GetNode(new TreePath(args.Path));
host.Enabled = !host.Enabled;
Process process = new Process();
process.StartInfo.UseShellExecute = false;
if (host.Enabled)
{
process.StartInfo.FileName = "gksu";
process.StartInfo.Arguments = "a2ensite " + System.IO.Path.GetFileName(host.FilePath);
}
else
{
process.StartInfo.FileName = "gksu";
process.StartInfo.Arguments = "a2dissite " + System.IO.Path.GetFileName(host.FilePath);
}
process.Start();
}
AFAIK, it is a security feature of 'su' not to cache the password (more properly the authentication ticket) for more than a few seconds, and thus this is designed not to be bypassed.
You can always gksu an intermediary process and try to make the sub-processes inherit its authorization, but you'll need to secure the IPC (the communication channel between your tool frontend and the intermediary process).
So my advice is to not try to lessen the security of the overall solution, so let the user be asked as many times as needed...
There are several (at least 3) "secure" solutions to this problem:
Use "sudo", that allows for password caching. This is my preferred solution if I can install and configure sudo on the machine. Pros: sudo will cache the password. Cons: it depends on having an external dependency (sudo) correctly configured.
Write different helper executables (for example a "modify configuration and restart apache" program) and when needed ask the user to authenticated using gksu, then launch them. Pros: user gets asked for the password only once for every group of actions. Cons: The user still get asked for the password multiple times AND you have to split the program in multiple pieces.
Write a separate service that runs with root privileges and use polkit/DBUS to authenticate the user and connect to it to require services (like, "restart apache please"). Pros: credential caching and authentication dialog is managed by dekstop/polkit. Cons: more code to write and you need to run a DBUS service.
"secure" is quoted because running code (and especially managed code that depends on a large application such as Mono) as root always has security implications.
Thanks for the useful approaches. Yesterday I arrived at a solution of the problem, which I have enclosed in a static class. First, a distinction is made between normal and privilligierten processes.
Whenever a process needs to be run with elevated privileges, I check if I know the user's password already. If not, I'll get it (gksudo -p) and store it in memory.
Now I can execute commands with privilligierten rights. The stored password is then transferred via the standard input (sudo -S).
What do you think? Do you have any safety concerns?
public static class SystemProcess
{
private static string output;
private static string error;
private static string password;
public static void Start (string filename, string arguments)
{
ProcessStartInfo startInfo = SystemProcess.Prepare(filename, arguments);
using (Process process = Process.Start(startInfo)) {
SystemProcess.output = process.StandardOutput.ReadToEnd();
SystemProcess.error = process.StandardError.ReadToEnd();
process.WaitForExit();
}
}
public static void StartPrivileged (string filename, string arguments)
{
ProcessStartInfo startInfo;
if (SystemProcess.password == default(string))
{
startInfo = SystemProcess.Prepare("gksudo", "-p true -D 'MyApplication'");
using (Process process = Process.Start(startInfo)) {
SystemProcess.password = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}
}
startInfo = SystemProcess.Prepare("sudo", "-S " + filename + " " + arguments);
using (Process process = Process.Start(startInfo)) {
process.StandardInput.WriteLine(SystemProcess.password);
SystemProcess.output = process.StandardOutput.ReadToEnd();
SystemProcess.error = process.StandardError.ReadToEnd();
process.WaitForExit();
}
}
private static ProcessStartInfo Prepare (string filename, string arguments)
{
ProcessStartInfo startInfo = new ProcessStartInfo (filename, arguments);
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
return startInfo;
}
public static string Output {
get {
return SystemProcess.output;
}
}
public static string Error {
get {
return SystemProcess.error;
}
}
}
You know, I'd not use gksu or sudo for any of this, sounds like you want to look at userv instead.
You can basically permit different users to run different programs,
I'm running a batch file from some ASP.NET/C# code on a web server. Basically the batch file performs some test automation tasks on a VM using tools like psloggedon and pexec.
If I run the batch file manually when I'm logged into the server under an administrative account, it works fine.
My problem comes when I run it from my code (below), it seems to run under the 'SYSTEM' account, and psloggedon etc. don't seem to work correctly.
Code
Process p = new Process();
p.StartInfo.FileName = "C:\SetupVM.bat";
p.Start();
p.WaitForExit();
I've got this in my web.config, it doesn't seem to make any differance?
<identity impersonate="true" userName="Administrator" password="myadminpassword"/>
Is there anyway I can ensure the batch file runs under the 'Administrator' account?
UPDATED CODE
Process p = new Process();
p.StartInfo.FileName = "C:\\SetupVM.bat";
p.StartInfo.UserName = "Administrator";
p.StartInfo.UseShellExecute = false;
p.StartInfo.WorkingDirectory = "C:\\";
string prePassword = "myadminpassword";
SecureString passwordSecure = new SecureString();
char[] passwordChars = prePassword.ToCharArray();
foreach (char c in passwordChars)
{
passwordSecure.AppendChar(c);
}
p.StartInfo.Password = passwordSecure;
p.Start();
p.WaitForExit();
From MSDN:
When UseShellExecute is false, you can start only executables with the Process component.
Maybe this is the issue as I'm trying to run a .bat file?
Thanks.
You can provide the username and password to the StartInfo:
Process p = new Process();
p.StartInfo.FileName = "C:\SetupVM.bat";
p.StartInfo.UserName = "Administrator";
p.StartInfo.Password = "AdminPassword";
p.Start();
p.WaitForExit();
See the documentation for ProcessStartInfo.