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.
Related
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.
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?
I know that there are a lot of questions concerning getting a
"System.UnauthorizedAccessException".
However I couldn't find a solution in any of these questions, as most of the answers refer to one of these Microsoft help pages.
My Situation:
I try to save some user input as .csv, so I can import it when needed.
My Code:
var csv = new System.Text.StringBuilder();
string dir = Path.Combine(Environment.GetFolderPath
(Environment.SpecialFolder.DesktopDirectory), "test.csv");
var newLine = string.Format("{0},{1},{2},{3},{4}", txtFirstName.Text, txtLastName.Text, txtEmail.Text,
txtPhone.Text, txtPlace.Text);
csv.AppendLine(newLine);
if (!File.Exists(dir))
{
using (FileStream fs = File.Create(dir))
{
Byte[] info = new System.Text.UTF8Encoding(true).GetBytes("FirstName,LastName,Email,Phone,Place");
// Add headers to the file.
fs.Write(info, 0, info.Length);
}
}
try
{
File.AppendAllText(dir, csv.ToString());
}
catch (Exception ex)
{
throw ex;
}
As you can see, I'm trying to write everything to my Desktop, in a file called "test.csv". I am running Visual Studio as an Administrator and the file I have on my Desktop is not read-only.
Does anybodoy have an idea why this still fails?
Edit: I'm running this as a Standard UWP-App on a desktop Computer.
From a UWP process file access is restricted. In order to write to the desktop (or any arbitrary location) your app will need to use the file save dialog and let the user confirm/choose the location. Then you will be able to save to the desktop or whatever location the user has decided to select.
In the upcoming Spring 2018 update for Windows 10 we will introduce a new capability ('broadFileSystemAccess') for UWP applications that will make this better. If you declare this capability in your manifest, the app will ask for user consent on first launch for broad file system access, and then you will be able to access all locations that the current user has access to.
If you need a solution that works on earlier versions of Windows 10 (prior to Spring 2018 update) and the file dialog is not a viable option then you can look into adding a fulltrust process to your UWP package that handles the file operations on behalf of your UWP process. You can launch that fulltrust process from the UWP via the FullTrustProcessLauncher API: https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.fulltrustprocesslauncher
Let me ask whether you can make a C# program which can not access to any local file system other than the folder/sub folders it is installed? And how if the answer is yes.
For details if it is necessary and possible, users are supposed to accept the program runs under a special user account which is restricted the file access only to the folder it is installed and to sub folders.
Thank you very much in advance.
Edit : Let me add context. I want users do not change their accounts. And as a programmer I do not have complete control over the program for some reasons.
Can you make a C# program which cannot access any part of the local file system other than the directory in which it is installed?
No, because every C# program will need to at the very least have access to the .NET runtime libraries, which are in the Windows install directory.
My suggestion to you is that you look into isolated storage. The .NET code access security system enables you to set a policy which states that certain programs only get to access the .NET runtime, the installed location of the code, and a special "isolated storage" directory that can be used for the application to store per-user data.
The answer is yes, but how you do this is complicated.
First, you need a user account with extremely limited permissions. It must be able to access files and run programs within the installation directory of the program, and that's pretty much it. You can create such a user with the installer program, using tools in the System.DirectoryServices namespace. Here's an example of creating a user:
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
private void CreateUser(string userName, string password)
{
DirectorySearcher dseSearcher = new DirectorySearcher();
string rootDSE = dseSearcher.SearchRoot.Path;
string userDSE = rootDSE.Insert(7, "OU=Users,");
DirectoryEntry userDE = new DirectoryEntry(userDSE);
DirectoryEntry user = userDE.Children.Add("CN=" + userID, "user");
staff.Properties["samAccountName"].Value = userID;
staff.Properties["UserPrincipalName"].Value = userName +
#"#domain";
staff.CommitChanges();
staff.Properties["userAccountControl"].Value =
ActiveDs.ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT |
ActiveDs.ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD;
staff.CommitChanges();
staff.Invoke("SetPassword", new Object[] { password });
}
Now, once that's happened, you need to make sure your program normally runs in the context of that user account. You can do that by specifying the user account to run the program with in a ProcessStartInfo object, used by a "bootstrapper" program that is what you create shortcuts for. You can then also ensure the program is running in the context of that user account using Environment.CurrentUser, and abort execution of the program if it is being run by any more permissive account.
If you just want to restrict the program to a particular account, you can look up the user's credentials, and exit the program if it's not the right one.
http://msdn.microsoft.com/en-us/library/csyx45b8.aspx
http://msdn.microsoft.com/en-us/library/sfs49sw0.aspx
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