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,
Related
Im making an application which needs to monitor the filesystem using FileSystemWatcher, to detect how an installation affects the filesystem.
To get rid of noise i want to filter the events that are created by their creating user, and that code is working with the //BUILTIN //Administrator user, which is used by default when doing an installation. But still there are quite a bit of noise. Then i got the idea of creating a specific user that i can use for running the installation file, and filter on that specific user, and thereby getting rid of allmost all the noise.
this is my code for the process creation and start
private void executeInnoInstaller(string path, string fileName)
{
// Use ProcessStartInfo class
ProcessStartInfo installerProces = new ProcessStartInfo();
installerProces.CreateNoWindow = true;
installerProces.UseShellExecute = false;
installerProces.FileName = path + "\"" + fileName + "\"";
installerProces.WindowStyle = ProcessWindowStyle.Normal;
installerProces.UserName = "test";
System.Security.SecureString encPassword = new System.Security.SecureString();
foreach (System.Char c in "test")
{
encPassword.AppendChar(c);
}
encPassword.MakeReadOnly();
installerProces.Password = encPassword;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(installerProces))
{
exeProcess.WaitForExit();
//int exitCode = exeProcess.ExitCode;
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
this code exits with a access denied.
OS=Windows
Ive already tried to run the installer.exe from the OS filehandler with SHIFT - Rightclick using the specified user, and it works.
VisualStudio is run as administrator.
Ive tried to run the build project exe file as administrator, but it does not work.
Without the user credentials, the code works and uses the //BUILTIN //Administrator account
Does anybody have any idea ?
Thank you beforehand for your time and effort.
This code works if i turn down the UAC securitylevel to the lowest.
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 have a console app I've built in VS2012 using C#. The EXE is located on a shared drive and can be run by any user, but I always want the EXE to run using a specific system account we have setup in AD. Essentially, I want to programmatically imitate right-clicking the EXE and doing "Run As..." instead of running under the current user who started it.
How can I force my code to always run under a specific account/password?
I wrote this for one of my app. Hope it can help you ;)
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// Launch itself as administrator
ProcessStartInfo proc = new ProcessStartInfo();
// this parameter is very important
proc.UseShellExecute = true;
proc.WorkingDirectory = Environment.CurrentDirectory;
proc.FileName = Assembly.GetEntryAssembly().Location;
// optional. Depend on your app
proc.Arguments = this.GetCommandLine();
proc.Verb = "runas";
proc.UserName = "XXXX";
proc.Password = "XXXX";
try
{
Process elevatedProcess = Process.Start(proc);
elevatedProcess.WaitForExit();
exitCode = elevatedProcess.ExitCode;
}
catch
{
// The user refused the elevation.
// Do nothing and return directly ...
exitCode = -1;
}
// Quit itself
Environment.Exit(exitCode);
}
}
}
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;
}
In an application I need to execute other programs with another user's credentials. Currently I use System.Diagnostics.Process.Start to execute the program:
public static Process Start(
string fileName,
string arguments,
string userName,
SecureString password,
string domain
)
However this function does not load the roaming profile from the net - which is required.
I could use "runas /profile ..." to load the profile and execute the command, but that would ask for a password. There must be an more elegant way...
But where?
My solution (based on leppie's hint):
Process p = new Process();
p.StartInfo.FileName = textFilename.Text;
p.StartInfo.Arguments = textArgument.Text;
p.StartInfo.UserName = textUsername.Text;
p.StartInfo.Domain = textDomain.Text;
p.StartInfo.Password = securePassword.SecureText;
p.StartInfo.LoadUserProfile = true;
p.StartInfo.UseShellExecute = false;
try {
p.Start();
} catch (Win32Exception ex) {
MessageBox.Show("Error:\r\n" + ex.Message);
}
System.Diagnostics.ProcessStartInfo.LoadUserProfile