System.Net.WebClient does not handle cache when launched from SYSTEM account - c#

I am having trouble with a script I am writing that is using System.Net.WebClient (called from Powershell but I guess the problem should occur with everything that is using the same cache as System.Net.WebRequest):
For context (as there may be a better solution than what I found):
I made an extension for IE (yes, some clients still use it) in C# (yes, it's not recommended but I had no choice)
this extension needs to run with EPM activated (so low-privileged).
it needs a configuration file that is available on a server accessed by HTTPS.
the configuration needs to be available when IE is launched so we have to cache it (also, each tab has its own instance of the extension)
that cached configuration have to stay in a privileged folder (the extension injects code to some of the pages according to that configuration, so you don't want the user or any process to have write access to it)
To solve the problem of caching the configuration, I wrote a Powershell script that is launched through the task scheduler. The script uses System.Net.WebClient to download the file, and I set it to respect the cache of the file:
$webclient = New-Object System.Net.WebClient
$cacheLevel = [System.Net.Cache.RequestCacheLevel]::CacheIfAvailable
$webclient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy($cacheLevel)
When I launch the script using "Run As Administrator", the cache is respected (providing the server is well configured).
When I launch the script from the task scheduler (user NT AUTHORITY\SYSTEM, as I need privilege to be able to save the file in the extension installation dir), the cache is not respected and the file is downloaded every single time.
Any idea on how to solve this issue? I need the caching to able to be poll the file without having to do a full download (the file is small, but the number of users is high :D).
Maybe it would be possible to use the date of the file that was previously downloaded?

Related

Application can find 'Downloads' folder when debugging but not on IIS

I have an application that allows the user to upload a file (saving it to in a folder located in the wwwroot of the ASPNETCORE application). From here they can make edits to it and then they can choose to export the file as a csv/ xml/ xlsx which downloads the file to the user's 'downloads' folder.
While debugging in Visual Studio this all works fine however when I publish and deploy the application to IIS I am getting the exception
Error saving file C:\windows\system32\config\systemprofile\Downloads(FILE NAME)
Could not find part of the path C:\windows\system32\config\systemprofile\Downloads(FILE NAME)
This is the current way I am getting the downloads folder:
FileInfo file = new FileInfo(Path.Combine(Environment.ExpandEnvironmentVariables(#"%USERPROFILE%\Downloads"), data.Filename + "." + data.FileType));
However I have also tried the solution that Hans Passant has answered to a similar question here. Both solutions worjk fine while debugging locally however as soon as I publish them, this one produces the exception:
Value cannot be null. Parameter name: path1
Which I presume is thrown at this point here when I try and save the file to the user's download folder.
using (var package = new ExcelPackage(file))
{
var workSheet = package.Workbook.Worksheets.Add("ExportSheet");
workSheet.Cells.LoadFromCollection(exports, true);
package.Save();
}
I don't really know how I would be able to reproduce these exceptions seeing as locally using Visual Studio it all works fine.
Has anyone else came across this issue while trying to download a file?
UPDATE: When the application is running on IIS, it seems to be using that as the user profile instead of the actually user, so when it tries to navigate to the Downloads folder, it cannot find it. How can I force it to use the user's profile?
LoadUserProfile is already set to True.
Web applications have no knowledge of the end-user's computer's filesystem!
So using Environment.GetFolderPath or Environment.ExpandEnvironmentVariables in server side code will only reveal the server-side user (i.e. the Windows Service Identity)'s profile directories which is completely separate and distinct from your web-application's actual browser-based users OS user profile.
As a simple thought-experiment: consider a user running a weird alien web-browser on an even more alien operating system (say, iBrowse for the Amiga!) - the concept of a Windows-shell "Downloads" directory just doesn't exist, and yet here they are, browsing your website. What do you expect your code would do in this situation?
To "download" a file to a user, your server-side web-application should serve the raw bytes of the generated file (e.g. using HttpResponse.TransmitFile) with the Content-Disposition: header to provide a hint to the user's browser that they should save the file rather than try to open it in the browser.

Downloading an exe without asking for administrator privileges C#

For the .NET Framework program I'm working on, I have an automatic updater set-up. It downloads a 7-zip Self-Extracting archive (in the form of an exe) from the latest github release, and runs it to update the program. The issue is, despite the fact that the program doesn't get installed anywhere that requires administrator privileges, the Update.exe always requires an admin to run it once installed, despite not being necessary when downloading it via browser. Is there any way to prevent this? Here's my current setup:
using (WebClient client = new WebClient()) {
// Add the user agent header, otherwise we will get access denied.
client.Headers.Add("User-Agent: Other");
// Full asset streamed into a single string. I.E: https://github.com/soopercool101/BrawlCrate/releases/download/BrawlCrate_v0.14_Hotfix2/BrawlCrate.v0.14.6873.39137.exe
string html = client.DownloadString(Asset.Url);
client.DownloadFile(URL, AppPath + "/Update.exe"); // AppPath is defined as whereever the program is installed
}
As according to Daniel, it was the name "update.exe" that was causing Windows to assume it needed admin privileges. Downloading it as "temp.exe" instead causes it to no longer prompt for admin approval.

Changing virtual path and enabling/disabling sites in code

I am working on a web project that contains three web services and a website. My source code is stored in TFS for version control and I regularly have to work in several different branches on the same code. These branches are merged regularly and are all set up in the same way. They all use WebDev.WebServer as default host. Which is fine because it saves other teammembers from having to continuously alter settings in IIS to switch to the proper folder.
Well, guess what?
I want to move to IIS and still be able to use the same config for every branch. This will results in conflicts since I Need to change the virtual folders for these projects every time when I switch branches. I also need to be able to turn off these IIS sites to enable the use of WebDev. Manually, that's a bit of work and I'm a programmer. This needs to be automated...
My idea is to create a small web application running on my local host, which I can use to change the virtual folder and/or to turn on/off the IIS site. Sounds simple enough so all I need are two things:
How do I change the virtual folder of an IIS site from one location to another in code?
How to turn on and off a site in IIS?
Simple, right? So, point three: do you have any other suggestions that I could use?
Oh, other developers are also working on the same project. Not all of them will use IIS to run it. Thus I cannot alter the config files for these projects. I must manage it all through IIS.
Personally, I would use Powershell in this instance.
$websitePath = "IIS:\\Sites\Path to your website in iis"
$fullWebSiteFilePath = "file path to your content"
if(-not (test-path $websitePath))
{
new-item $websitePath -physicalPath $fullWebSiteFilePath -type Application
}
else
{
Set-ItemProperty $websitePath -name physicalPath -value $fullWebSiteFilePath
}
with a little jigerry pokery you could read the different configurations from an xml file and then call the shell script passing the xml file name as a parameter.
To manage IIS7 programmatically, you can start with the ServerManager class.

Determining Write Permissions to the Application Folder

I have a C# application, and I need to dump some output to a log file during operation. I am wanting to give the user the option of where to locate the log file, but by the client request it needs to default to the current application location, which is normally /Program Files/.
When I deploy my application on a Win7/Vista machine, though, the application does not write the log file unless I run the program as an Administrator. At the same time, it seems to be silently handling the case where it cannot write the file, as I am currently handling all exceptions being thrown during the file creation and writing process.
I am currently trying to detect lack of write permission by both:
A) Creating a DirectorySecurity object by calling "Directory.GetAccessControl()" and
B) Checking security priviledges with the "SecurityManager.IsGranted(permissions)" method,
but A does not throw an exception when I expect it to, and B returns true every time.
I have seen numerous posts related to this topic, but they all give the solution of just writing to Application.UserAppDataFolder or some variation of it. My client has specifically asked to default to the current Application path, so I need to at least find a way to gracefully warn them when writing the log file is going to silently fail.
Note: My current code works find on Windows XP (since there are no UAC, I assume). Basically all I need to know is why all my calls are telling me that writing the file is going fine, when the file is never created at all unless I am running as Admin.
Thanks!
Windows Vista and 7 will write files to the Program Files directory just fine.
Well, not really, but the program thinks it's just fine. In reality, the file is written to the current user's VirtualStore directory; that is, in %userprofile%\AppData\Local\VirtualStore\Program Files
You can include a manifest file to disable this behavior for your application to get the results you expect.
You can force the os to run your app as Admin.
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
There are three ways your app can run - elevated, deliberately not elevated (manifest saying asInvoker), or accidentally not elevated (no manifest). Elevated apps will be able to write to Program Files. Deliberately not elevated apps will get access denied. Accidentally not elevated apps will succeed but the file will be written elsewhere. This last case is what's happening to you. It didn't silently fail. You just don't know where the files are. See http://www.gregcons.com/KateBlog/FindingFilesYoureSureYouWrote.aspx for screenshots.
Therefore if the users insist on the current directory, you should add a manifest requesting asInvoker. You will then get AccessDenied and they will see the error message. I think they are odd for wanting this. Ask them if they are ok with one extra click to find them: if so, keep your app using virtualization (I really disapprove) by having no manifest and then train them to click the Compatibility Files button.
My preference: write elsewhere and manifest to asInvoker. My second choice: stick with current directory, no manifest, train them to find virtualized files. My third choice: stick with current directory, manifest to asInvoker, users see error messages when log files are not written, but logs are lost.
I am experiencing the same problem. I have an xml file that i am writing to...When I install the app(C sharp) and try to run the application am getting an exception due to write permission. When I change the file permission (give read permission to users) it is working ok..
The ultimate test for whether you have the rights to write a file is to open it for writing.
I.e.
try
{
File.Open(path, FileMode.OpenOrCreate);
...
}
catch(SecurityException)
{
... it failed for security reasons
}
catch(Exception)
{
... it failed for other reasons
}
Besides Stefan P.'s suggestion to elevate the app to run as admin, you could also modify the installation folder permission on install to to add the Users group to have write access. Then the application would work as well.
Moving the log file location would be the best option though.

C# Application can't read/write to files created by administrator when run in limited user account XP

I have an application that is useable by all users (admin or limited) in .NET (C# specifically).
When the application first launches - it creates a few files that it needs in the C:\Documents and Settings\All Users\Documents\ for all subsequent launches.
If the limited user in XP is the FIRST user to launch the application it creates the files fine and both the limited user and administrators can run fine.
However if the Administrator (or I am guessing a different limited user) is the first to launch the application then the limited user is NOT able to run the application.
The two files that it is NOT able to read/write to if created by an Administrator is a Log4Net log file and a SQLite db file.
The SQLite database file is being created with a straitforward .NET File.Copy(sourcepath, destinationpath). The sourcepath is a seed database file installed with the application - so on first run it copies that from the C:\Program Files\app install\seed.db
Is there a way to set the permissions on the file when I copy it? File.SetAccessControl() perhaps? I am not clear on how that works.
The other issue is that the log4Net rolling file appender will not roll the old file and create a new as the old file was created by the admin user when they ran the app.
Any ideas? Ironically this all works perfectly fine in Vista with limited/admin accounts - this is ONLY happening in XP with admin/limited accounts.
I think SetAccessControl is the way to go. Maybe something like this:
// get the existing access controls
FileSecurity fs = File.GetAccessControl(yourFilename);
// add the new rule to the existing settings
fs.AddAccessRule(new FileSystemAccessRule(
#"DOMAIN\Users", // or "BUILTIN\Users", "COMPUTER\AccountName" etc
FileSystemRights.Modify,
AccessControlType.Allow));
// set the updated access controls
File.SetAccessControl(yourFilename, fs);
Note: It's important that you get the existing access control list from the file and then add your new rule to that. If you just create a new access control list from scratch then it will overwrite the existing permissions completely.
Yeah, it's the SetAccessControl method all right, there is a good example here
(the post from satankidneypie)
Good luck

Categories