C# Remove Readonly From Main Application Folder - c#

I'm trying to build an application with an auto / self updater. The file will check for updates then immediately download the files and replace the necessary files. I've been trying to put this into an installer package, but am running into a problem with the Application Folder being read only. I've tried removing the readonly parameter using code from multiple SO this one, but after the program is installed, the folder remains read only.
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
try
{
string path = this.Context.Parameters["targetdir"];
path = path.Substring(0, path.Length - 1);
DirectoryInfo di = new DirectoryInfo(path);
di.Attributes &= ~FileAttributes.ReadOnly;
di.Refresh();
}
catch (Exception e)
{
}
}
I've tried putting this in the Commit method as well. The path is definitely being pulled (MessageBox.Show showed the correct path).
Do I need to be doing something different to change the application's main folder?
I don't see why the updater process matters in the context of this question, but here is how it is working:
User launches the updater app as a sort of "portal" to the main application.
The updater checks the server for an update specific to that device.
The new files are downloaded and all files are replaced that aren't currently locked.
The exe then calls a helper exe and closes itself. The helper exe updates the remaining files (IE the updater itself)
The helper then launches the main application.

You're not looking to remove the Read-only flag, you're looking for elevated permissions to write in that folder - it's not read only to begin with.
To that end you can run your installer app with "RunAs":
// launcher code
if (CheckIfUpdateAvailable()){
ProcessStartInfo startInfo = new ProcessStartInfo ("MyUpdater.exe");
startInfo.Verb = "runas";
System.Diagnostics.Process.Start (startInfo);
Application.Quit();
}
the process spawned by launcher will have the rights to write in your app folder
and your updater has to be an executable you deploy along with your app - you'll find it hard to overwrite a running executable's file
or you could switch to ClickOnce and this comes for free. Granted - there are some minor limitations on what you can do with a ClickOnce installer.

http://support.microsoft.com/kb/981778
I ended up doing a self-elevation (restart run-as) for the updater. This will only ask for permission if there is an update available.
// During update process:
if (!IsAdministrator())
{
// Launch itself as administrator
ProcessStartInfo proc = new ProcessStartInfo();
proc.UseShellExecute = true;
proc.WorkingDirectory = Environment.CurrentDirectory;
proc.FileName = Application.ExecutablePath;
proc.Verb = "runas";
try
{
Process.Start(proc);
}
catch
{
// The user refused to allow privileges elevation.
// Do nothing and return directly ...
return false;
}
Application.Exit();
}
public static bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
Another solution that is working, although not entirely how I would like it, is to go through each project's properties -> Security -> Enable ClickOnce security settings and then building the installer. This is annoying because it asks for permission every time the file runs on a UAC account. However, it is working and doesn't require some looping exe launching.

Related

MVC: start process as different user - not working

I need to run an executable on the server from an MVC controller. Problem: the executable sits within Program Files folder and will also read a value from registry.
I have granted execution rights on the respective folder to my application pool.
So here's my problem:
Running the exe just with Process.Start(exe) will start the executable which in turn then exits with an error because it cannot read the registry value (no access).
Assigning a local admin user to ProcessStartInfo fails:
var exe = #"C:\Program Files (x86)\[path to exe]";
var secString = new SecureString();
secString.AppendChar('character');
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
UserName = "[username]",
Domain = "[domain]",
Password = secString,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Verb = "runas"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
This will cause a crash of conhost and the executable.
Using impersonation like this:
var impers = new ImpersonationService();
impers.PerformImpersonatedTask("[user]", "[domain]", "[password]",
ImpersonationService.LOGON32_LOGON_INTERACTIVE, ImpersonationService.LOGON32_PROVIDER_DEFAULT, new Action(RunClient));
...with the method RunClient() simply using Process.Start(exe) will do absolutely nothing! The method is run but the process is not being started. I know that the method is run because I added this line to it:
_logger.Debug("Impersonated: {0}", Environment.UserName);
Which correctly gives me the desired user name the process shall use. That user has local Admin privileges, so there should not be an issue there.
I have even tried starting a different executable from my Controller and have that one use impersonation (both variants) to start the target executable - same outcome.
So right now I'm at a dead end. Can anyone please tell me what I'm doing wrong and what I have to do to make it work?
P.S: running the target executable directly on the server when logged in as the local admin user works perfectly fine, so no prob with the exe itself.
Edit:
It seems one part of my description was incorrect: with impersonation and RunClient method I actually did not use Process.Start(exe) but this:
var procInfo = new ProcessStartInfo(exe, settingsPath)
{
UseShellExecute = false,
};
_logger.Debug("Impersonated: {0}", Environment.UserName);
var proc = Process.Start(procInfo);
Out of desperation I have now circumvented procInfo(don't actually need it) and really called
var proc = Process.Start(exe, argument);
And now the .exe starts! It seems using ProcessStartInfo overrides the impersonation for the process??
Still not OK though, as now I get an "Access denied" error. Despite being local admin. This is just weird.
Edit 2:
This is how my latest attempt went:
Switched back to calling a helper .exe, passing the same arguments later used for the actual target exe in Program Files
added a manifest to that helper exe with level="requireAdministrator"
Added Impersonation to my helper exe according to https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx with [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] added before the method starting the target process.
Started the process by providing ProcessStartInfo with all the jazz
Resulting code:
try
{
var secString = new SecureString();
//...
secString.MakeReadOnly();
var procInfo = new ProcessStartInfo()
{
FileName = Path.GetFileName(exe),
UserName = "[UserName]",
Domain = "[domain]",
Password = secString,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
Arguments = settingsPath,
WorkingDirectory = #"C:\Program Files (x86)\[rest]"
};
var proc = Process.Start(procInfo);
proc.WaitForExit();
if (proc.ExitCode != 0)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}", proc.ExitCode.ToString());
}
}
}
catch (Exception ex)
{
using (var sw = new StreamWriter(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "error.log"), true))
{
sw.WriteLine("Error running process:\r\n{0}\r\nRunning as: {1}", ex.ToString(), WindowsIdentity.GetCurrent().Name);
}
}
Resulting output to error.log:
Helper running!
[passed argument]
Error running process: System.ComponentModel.Win32Exception
(0x80004005): Access denied at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at System.Diagnostics.Process.Start() at
System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at
RunClient.ImpersonationDemo.RunClient(String settingsPath)
Running as: [correct domain user in Admin group]
So I can start the helper exe but that cannot start the real exe in Program Files due to Acess denied despite running under a local Admin account and all files access locally, not on network drives.
The logic of this eludes me.
Edit 3
Update: I have added a manifest to the target .exe also,
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
This means I now:
Call a helper exe from the controller: works
The helper .exe has a manifest to run with elevated rights (Admin)
The helper .exe uses impersonation to assume the identity of a local admin to start a process
Said process is started using a ProcessStartInfo in which username, domain, and password are additionally set to the same local admin user
The helper exe then tries to run the target exe using Process.Start(Startinfo) with the local admin user set, while still impersonating that user's windows identity
And still the error log spouts "Access denied" while correctly returning WindowsIdentity.GetCurrent().Name as that of the local admin.
And now, the greatest of all happened: I created a new local user on that server, added him to local admin group and used that user for impersonation just in case there is a problem with domain users. Guess what? Now I get an error Access denied to ...\error.log - written to the effing error log.
Really?
Edit 4
I think I'll try TopShelf to convert this shebang to a service. Hope to get this done over the weekend.
According to this article your mvc controller thread should have full-trust permission to run the process:
This class contains a link demand at the class level that applies to
all members. A SecurityException is thrown when the immediate caller
does not have full-trust permission. For details about security
demands, see Link Demands.
Seems you problem is not the user but full-trust. I do not know which version of MVC you use but you can read the articles Trust Levels and Code Access to find out the best way to configure your application. Seems you can grant full-trust permission only to specific .exe file or grant full-trust permission to application pool user (do not forget about folder permissions).
But the best approach is to write some windows service and run it instead of running some .exe file directly.
You can try trust level in web.config of your application
<system.web>
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
</securityPolicy>
</system.web>

c# Deleting a file in System32 folder

im trying to delete a specific file in system32 but as thats where all the important files is, its very hard to delete things from there. The only way i can do it so far is if i go into the propertys and change the permissions so i have full control over that file and then delete it.
I am trying to do this programmaticly, ive tried numerous ways of changing the permissions on a file but i just get the error "unauthorised", even in elevated cmd using commands like takeown/ it doesnt work, i know if i can do it in cmd, i can do it programmaticly so that would work too.
any help is appriciated
You should elevate the permissions due to UAC. Below is a sample which checks if the current user running the app has elevated rights, if not, exits and re-runs the app with Admin rights.
WindowsPrincipal pricipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = pricipal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
RunElevated(Application.ExecutablePath);
this.Close();
Application.Exit();
}
private static bool RunElevated(string fileName)
{
//MessageBox.Show("Run: " + fileName);
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
processInfo.FileName = fileName;
try
{
Process.Start(processInfo);
return true;
}
catch (Win32Exception)
{
//Do nothing as user cancelled UAC window.
}
return false;
}
Take ownership of the file and delete
new Process() { StartInfo = new ProcessStartInfo("cmd.exe", #"/k takeown /f C:\Windows\System32\testfile.txt && icacls C:\Windows\System32\testfile.txt /grant %username%:F") }.Start();
File.Delete(#"C:\Windows\System32\testfile.txt");
Further research showed that there is a much better way of doing this. Carry on;
Create Application Manifest
By doing this, you don't need to check for admin rights in the app and restart if required. By default, app will launch with the elevated credentials. Simple Add > New Item > Application Manifest to project and uncomment following line ;
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Set File Access Permission in Code and Delete File
There should be a lincense.txt file in System32 folder. We can delete it with the following;
FileInfo insInfo = new FileInfo(#"C:\Windows\System32\license.txt");
FileSecurity insFileSecurity = insInfo.GetAccessControl();
insFileSecurity.AddAccessRule(new FileSystemAccessRule(System.Environment.UserDomainName + "\\" + System.Environment.UserName, FileSystemRights.FullControl, AccessControlType.Allow));
insInfo.SetAccessControl(insFileSecurity);
File.Delete(#"C:\Windows\System32\license.txt");
NOTE : Once you set the application.manifest with requirement of elevated permissions, Visual Studio may want to restart with Administrator rights when you start debugging. Simply allow it to restart, your project will be loaded automatically and you can start debugging.
NOTE FOR CLICK-ONCE APPLICATIONS : When elevated permissions are set in the manifest, clickonce applications deny to compile. To fix this, right click on the application project and go to Security tab. Uncheck "Use Click-once Application Security" option and you are good to go.
Hope this helps

How to start an exe from a .NET Windows Service for updating the service

I have a windows service that I would like to be automatically and silently updated. I started using wyBuild to implement this, but have had some issues with it, and decided to try to build my own. I've written a standalone exe that can be called to do the update procedure: checks for a new zip file with the update, downloads it, unzips, stop the windows service, copy files from the zip, then restart the service. This exe works fine when I run it from the commandline and wasn't really difficult to write.
However, now I would like the service (the same one being updated) to shell out to the updater exe to update itself. I first tried Process.Start:
var proc = Process.Start(pathToUpdaterExe);
proc.WaitForExit(60000);
This called the updater, but when the updater stops the service, the process is killed and the update stops. I did some searching and it sounds like the solution is to use a separate AppDomain. This is what I have now:
Evidence baseEvidence = AppDomain.CurrentDomain.Evidence;
Evidence objEvidence = new System.Security.Policy.Evidence(baseEvidence);
AppDomainSetup setup = new AppDomainSetup();
var updateDomain = AppDomain.CreateDomain("updateDomain", objEvidence, setup);
updateDomain.ExecuteAssembly(updater);
AppDomain.Unload(updateDomain);
However, now I get the error System.IO.IOException: "The process cannot access the file 'C:\Program Files (x86)\Company\Service\Service.dll' because it is being used by another process" when attempting to copy over the new Service.dll
Again, I've stopped the service at this point. I've confirmed this with logging. I can't imagine what would have Service.dll still locked, so I added code to check to see what is locking it:
public static IEnumerable<Process> GetProcessesLocking(string filePath)
{
var result = new List<Process>();
result.Clear();
var processes = Process.GetProcesses();
foreach (Process proc in processes)
{
try
{
if (proc.HasExited) continue;
foreach (ProcessModule module in proc.Modules)
{
if ((module.FileName.ToLower().CompareTo(filePath.ToLower()) == 0))
{
result.Add(proc);
break;
}
}
}
catch (Exception ex)
{
Log(ex.ToString());
Log("There was an error checking " + proc.ProcessName );
}
}
return result;
}
However this code indicates that nothing has a lock on the dll (result is empty and nothing is logged indicating an error).
I suspect I'm running afoul of some UAC issue that is the real cause of the IOException. The windows service runs as LocalSystem. All that to ask: How should I be running the update exe from the windows service such that it has rights to copy files in c:\Program Files?
Update
As the comments and answer suggest, Process.Start can work, but there is some nuance. You have to start cmd.exe and use it to start the updater. I also found I could not use a full path for the updater exe and that I needed to set UseShellExecute=false. This is my final working code that launches the updater from the .NET service:
var cmd = "/c start updater.exe";
var startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = cmd;
startInfo.WorkingDirectory = AssemblyDirectory;
startInfo.UseShellExecute = false;
var proc = Process.Start(startInfo);
I did this exact thing - using a simpler (some might say kludgy) approach. The service:
Produces a batch command,
Downloads the new executables to a staging location,
Starts a process: cmd.exe which, in turn, runs the batch script w/o waiting for it to complete, and then
Immediately terminates itself.
The batch command:
Pings 127.0.0.1 five times,
Copies the executables to the final location and,
Restarts the service.
Works like clockwork. The ping is a reliable 5 second delay - lets the service shutdown before copying the files.
Edit:
Just for completeness - I realized that by batch cmd is pinging 127.0.0.1 not 128.0.0.1 and so I edited this answer to reflect that. I suppose either works - but 128.0.0.1 pings timeout, where 127.0.0.1 resolves to "me". Since I'm only using it as a poor-man's delay, it serves the purpose either way.

Permission issue running .exe from ASP.NET web application

We have a .exe which we need to execute at the time an order is placed on a website. When
we test this locally it works fine using IIS Express. When we move it to IIS, it fails. We assume this is a permissions error as if we set the App Pool to run as the administrator then the script works again. The question we have is how do we execute the .exe as the administrator whilst the App Pool is ApplicationIdentity? We are using the following code:
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = executablePath,
Arguments = argumentList,
Domain = domain,
UserName = userName,
Password = securePassword,
UseShellExecute = false,
LoadUserProfile = true
}
};
process.Start();
process.WaitForExit();
process.Close();
The .exe is trying to write to the Users AppData folder which is why it fails. It is a 3rd party app so we cannot change the source code.
EDIT: Just to clarify also, when we specify the username and password in procmon it still appears to run from ISUR.
We fixed this by enabling User profile on IIS AppPool and setting permission for the IIS user on the folder it was trying to write to.
We sue ProcMon to find where the program was failing and the folder it was trying towrite to was C:\Windows\System32\config\systemprofile
i dont remember actually, but one of them is working 100% ( i had this issue before)
just let me know ehich one of them is the correct one.

Run exe from webpage

We have an internal page that I want to use to run an executable that updates some files on the server. In other words, rather than logging in to the server every time I need to manually run this executable, I would like to run it from the browser. The executable is self-contained on the server and does not interact with the user.
Here is my code:
try
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = Server.MapPath(#"\iPhoneXMLCreator.exe");
p.StartInfo.WorkingDirectory = Server.MapPath(#"\");
p.StartInfo.RedirectStandardOutput = false;
p.Start();
p.WaitForExit();
lblResult.Text = "Success!";
}
catch (Exception ex)
{
lblResult.Text = "Oops, there was a problem.<br><Br>" + ex.Message;
}
When I run it, the process shows up in Task Manager, but then exits within a few seconds without updating the files it is supposed to. There are no arguments to be passed, just a simple executable. Any ideas?
I would start by checking to see if the account which runs the web application has the appropriate permissions.
Most likely this is a permissions issue. Since it's the Asp.Net runtime that is executing this program, you need to ensure that the user account that the Asp.Net runtime uses has access to this executable, and to modify any resources (files, databases, etc) that get modified by the executable.
You can do this via impersonation, or by granting rights tot he appropriate accounts. The proper approach is to use impersonation.
http://msdn.microsoft.com/en-us/library/xh507fc5.aspx
Does the executable file run and process the XML when you run it manually on the server logged in as yourself?
Then it may be a simple permissions issue, since unless you are impersonating ...it's probably trying to run the exe under the ASPNET machine account, which most likely doesn't have rights to the folder the XML is in. Just a thought based on the info you provided.
2 things that you could do:
Run Process Monitor while you attempt to run the exe. I've used it many times to help to find security config problems (especially on web servers). It will log every io and registry access, and more importantly indicate success or failure. Get it here: http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx It requires no setup. Great tool!
Redirect stdout on your console exe. This will allow you to capture any error message that it is attempting to write to the console. Here's how to do it:
try
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = Server.MapPath(#"\iPhoneXMLCreator.exe");
p.StartInfo.WorkingDirectory = Server.MapPath(#"\");
// redirect stdout
p.StartInfo.RedirectStandardOutput = true;
var ConsoleOutput = new StringBuilder();
p.OutputDataReceived += (s, e) => ConsoleOutput.AppendLine(e.Data);
p.Start();
p.BeginOutputReadLine(); // if I remember correctly, you have to call Start() first or you get an exception
p.WaitForExit();
string output = ConsoleOutput.ToString();
lblResult.Text = "Success!";
}
catch (Exception ex)
{
lblResult.Text = "Oops, there was a problem." + ex.Message;
}
Rather than playing with website permissions for the Exe, one workaround that uses a level of indirection and puts a buffer between your web site and the Exe is to simply set a flag value into a text file on the web server when the Page representing the Exe is hit.
Set up a scheduled job on the server to check for that flag value every X hours, or minutes, or whenever and if the flag is seen, run the executable. Reset the flag/file when done. This opens up the possibility to check the flag via a webservice or other mechanisms, such that the target Exe doesn't even need to be on the same web server machine.
This is only viable if the exe does not need to run immediately when the page is hit.
Ok, figured it out. It was a data access issue. The .config file for the .exe had an invalid database connection string. Why it would work when logged in, I'm not sure, but it works now.

Categories