Gaining access to a MemoryMappedFile from low-integrity process - c#

I'm trying to create a MemoryMappedFile on a medium-integrity process, then open the same file on a low-integrity child process and use this shared memory for IPC. There's no real disk file (using MemoryMappedFile.CreateNew).
My problem is that the low-integrity process cannot open the shared memory, throwing this: "System.UnauthorizedAccessException: Access to the path is denied.". I'm not surprised that this is the case, given that I want write access from the low-integrity process, but how do you grant it access?
Here's my code:
Medium integrity process:
MemoryMappedFileSecurity security = new MemoryMappedFileSecurity();
var file = MemoryMappedFile.CreateNew("test", 4096, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, security, HandleInheritability.Inheritable);
var view = file.CreateViewAccessor();
view.Write(0, true);
Low integrity process:
try
{
MemoryMappedFile file = MemoryMappedFile.OpenExisting("test", MemoryMappedFileRights.ReadWrite);
var view = file.CreateViewAccessor();
var v = view.ReadBoolean(0);
Log.Info("MAPPED: " + v);
}
catch (Exception e)
{
Log.Info("Error: " + e);
}
Works fine if both processes work in medium integrity. After reading this, I tried setting the SDDL string on the medium integrity process like this:
security.SetSecurityDescriptorSddlForm("S:(ML;;NW;;;LW)");
But that gives me another exception, this time when the memory mapped file is created: "System.IO.IOException: A required privilege is not held by the client.". Not really sure this is the right way to do it anyway, I'm not really clear on how the Win32/C++ examples translates to C#...
Anyone know anything more about this?

Okay, got a working solution. There were two problems:
Passing an empty MemoryMappedFileSecurity object to MemoryMappedFile.CreateNew() made the mapped memory inaccessible even to the same process. That explained my error in my comment ("System.UnauthorizedAccessException: Access to the path is denied").
I couldn't actually get security.SetSecurityDescriptorSddlForm to work (and even though google reveals several other attempts at this, none of them worked for me). Instead, I used this solution: https://stackoverflow.com/a/14424623/5105846. As far as I can tell, it does the same thing, but using PInvoke instead. So I just called InterProcessSecurity.SetLowIntegrityLevel(file.SafeMemoryMappedFileHandle), and it made it accessible from the low-integrity child process. Success!
Not the perfect solution, but a working one is all I need for now. Thanks Harry for your help!

Related

How do I programmatically give ownership of a Registry Key to Administrators?

I ran into a bizarre issue when I upgraded some machines to Windows 10 where incorrect permissions on RuntimeBroker caused problems. I found a solution online that recommended changing permissions (first in the registry, then in DCOM configuration), and I'm trying to write a small .NET application to automate the process.
Presently the owner of the relevant registry keys is NT SERVICE\TrustedInstaller and I'm trying to change it to COMPUTER\Administrators. I have a simple WPF application with the requestedExecutionLevel set to "requireAdministrator," but I'm still running into problems. Here's a snippet of code to illustrate the problem:
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.Win32;
namespace PermissionFixer
{
public class Fixer
{
public void Fix()
{
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", true);
if (subKey != null)
{
var admins = new NTAccount("Administrators");
var ac = subKey.GetAccessControl();
ac.SetOwner(admins);
ac.AddAccessRule(new RegistryAccessRule(admins, RegistryRights.FullControl, AccessControlType.Allow));
subKey.SetAccessControl(ac);
}
}
}
}
The trouble is that it doesn't even get past the call to OpenSubKey() before hitting a SecurityException that says "Requested registry access is not allowed." I think that's because Administrators doesn't yet have the access (remember it belongs to TrustedInstaller), but it becomes a bit of a chicken and egg problem. The strange thing is that when I use regedit by hand I am allowed to change the owner to Administrators, and I'm pretty sure my instance of regedit is running as Administrators.
How can I get this working in .NET?
I figured it out, and fortunately it is possible to achieve with the .NET classes. Here is how you have to call OpenSubKey:
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
Then you have to nix the call to AddAccessRule()... you can't modify that until you have ownership; and you have to do those two operations in serial. So take ownership first, then re-open the key with different access rights to add the access rule.
EDIT: I discovered today that you must first manipulate the token with which your application is running, by hooking into P/Invoke calls. I found a class called TokenManipulator referenced in another Stack Overflow question. Include that class in your project, and then grant Backup, Restore, and TakeOwnership privileges to your token before calling OpenSubKey. So your method will end up looking something like this:
try
{
TokenManipulator.AddPrivilege("SeRestorePrivilege");
TokenManipulator.AddPrivilege("SeBackupPrivilege");
TokenManipulator.AddPrivilege("SeTakeOwnershipPrivilege");
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
// code to change owner...
}
finally
{
TokenManipulator.RemovePrivilege("SeRestorePrivilege");
TokenManipulator.RemovePrivilege("SeBackupPrivilege");
TokenManipulator.RemovePrivilege("SeTakeOwnershipPrivilege");
}

The process cannot access the file 'filename' because it is being used by another process

I am new in C# and I have a problem connecting to a Firebird database. I want my program to access a Firebird Database [FDB format file]. I have problem, see the code below:
File.Copy(pathway, new_pathway, true);
FbConnection addDetailsConnection = new FbConnection("User=sysdba;Password=masterkey;Dialect=3;Database= " + new_pathway +
";DataSource=localhost;" );
string SQLCOMMAND = " SELECT UOM FROM ST_ITEM_UOM WHERE CODE = 'ANT'";
addDetailsConnection.Open();
FbCommand readCommand = new FbCommand(SQLCOMMAND, addDetailsConnection);
FbDataReader myreader = readCommand.ExecuteReader();
while (myreader.Read())
{
MessageBox.Show(myreader[0].ToString());
}
myreader.Close();
readCommand.Dispose();
addDetailsConnection.Close();
addDetailsConnection.Dispose();
This code lets me read my FDB file and extract the data. When the code executes for the first time, there is no error or problem, However when when I execute it again, this error is shown:
The process cannot access the file 'C:\Users\ACC-0001.FDB' because it is being used by another process.
You can use Handle to check which program is locking the file. It might be caused by your code or by another process running on your machine.
The tool identifies the process, for example:
C:>handle.exe c:\test.xlsx
Handle v3.46 Copyright (C) 1997-2011 Mark Russinovich Sysinternals -
www.sysinternals.com
EXCEL.EXE pid: 3596 type: File 414: C:\test.xlsx
As found here.
If the problem lies within your code, make sure you dispose and close all connections, preferably by using them within using sections:
using (FbConnection addDetailsConnection = new FbConnection("..."))
{
// do work
}
More details on using using can be found here.
You might have bumped into this Firebird issue: FB server reports that DB file is used by another application on secondary attachment attempt through a symlink
It only happens on Windows and only when two non-embedded connections use different path names of which one or both have a symlink in their path so they effectively point to the same location.
Both handle.exe and Process Explorer will only show the canonical (final) filename that fbserver.exe actually opens.
The only way to find out is to:
compare connection strings.
verify with handle.exe or Process Explorer that the files are indeed opened by fbserver.exe (and not by your process itself using an embedded connection)

Getting back variables after a program crashes

I am trying to find a way to get back my previous variable's value so that I can resume my application to it's previous running state before it crashed when I MANUALLY relaunch it. I am currently using a 'config' file that is saved in the project folder. Is there a better way to do this?
Some small parts of my code that I want to save.
if (EventID == WIA.EventID.wiaEventItemCreated)
{
if (d != null)
{
foreach (Property p in d.Properties)
{
if (p.Name.Equals("Pictures Taken"))
Console.WriteLine("Taken");
}
wiaImageFile = (WIA.ImageFile)(d.Items[d.Items.Count].Transfer(FormatID.wiaFormatJPEG));
wiaImageFile.SaveFile(Properties.Settings.Default.FolderNameRaw + "\\" + imageCount + ".jpg");
imageCount++;//I want to save this count so that I can continue the sequence even after the application crashes
Pluck.Default.PhotoExistsInDirectory = true;
FacebookControls fbc = new FacebookControls();
if(Properties.Settings.Default.UseFB == true)
fbc.UploadPhotos();
}
}
A config file is a good answer in general. Your other options are usually the registry or the database, but I would argue that a config file is a safer option.
The thing about persisting this information is that it may cause an error again, and if so you'll want to be able to discard it easily. A file (of course stored in user settings space) is perhaps the way to do that. If need be you can instruct the user to delete the file. It's a more complicated fix for a user to access the registry or the database.
Also, you should wrap up your state in an appropriate object, and build initialization logic that initializes the state object and has mechanism for not loading for the config file.
I use config files. I also have a global exception handler that catches any exceptions and offers the chance to save any files (including those that the user is working on) before the app closes.
I would also agree with C Ross that you may persist the data that caused the app to fail. Another option that will not get you right back is to persist the settings at regular intervals using a timer or background process. I use this with several backups a bit like the system restore feature in windows.
You can handle UnhandledException, Application_ThreadException and Application.ApplicationExit Event, and try saving your data there:
http://www.switchonthecode.com/tutorials/csharp-tutorial-dealing-with-unhandled-exceptions
As #C. Ross said, user config file is a good choice.
Of course, first you'll have to preserve your application's state in some object during runtime.

File existence check after a folder rename returns an incorrect value on UNC share

On a Windows 7 (or server) box, we have a folder on a UNC share (cross machine UNC, not localhost). We rename that folder, and then check for the existence of a file at the new folder location. Even though it exists, it takes almost 5 seconds for File.Exists to return true on it.
Full repro can be found on https://github.com/davidebbo/NpmFolderRenameIssue. Here is the core code:
// This file doesn't exist yet
// Note that the presence of this existence check is what triggers the bug below!!
Console.WriteLine("Exists (should be false): " + File.Exists("test/test2/myfile"));
// Create a directory, with a file in it
Directory.CreateDirectory("test/subdir/test");
File.WriteAllText("test/subdir/test/myfile", "Hello");
// Rename the directory
Directory.Move("test/subdir/test", "test/test2");
var start = DateTime.UtcNow;
// List the files at the new location. Here, our file shows up fine
foreach (var path in Directory.GetFiles("test/test2"))
{
Console.WriteLine(path);
}
for (; ; )
{
// Now do a simple existence test. It should also be true, but when
// running on a (cross machine) UNC share, it takes almost 5 seconds to become true!
if (File.Exists("test/test2/myfile")) break;
Console.WriteLine("After {0} milliseconds, test/test2/myfile doesn't show as existing",
(DateTime.UtcNow - start).TotalMilliseconds);
Thread.Sleep(100);
}
Console.WriteLine("After {0} milliseconds, test/test2/myfile correctly shows as existing!",
(DateTime.UtcNow - start).TotalMilliseconds);
So it seems like the initial existence check causes the existence value to be cached, causing this bogus behavior.
Questions: what is the explanation for this? What's the best way to avoid it?
NOTE: this issue initially arose when using npm (Node Package Manager) on Windows. The code I have here is a C# port of the repro. See https://github.com/isaacs/npm/issues/2230 for the original Node/npm issue. The goal is to find a way to address it.
David,
The redirector implements a negative "File Not Found" cache which prevents a client from flooding a server with file not found requests. The default cache time is 5 seconds but you can modify the FileNotFoundCacheLifetime registry value to control the cache or disable it by setting this value to 0.
Details: http://technet.microsoft.com/en-us/library/ff686200(v=WS.10).aspx
There are multiple levels of caching in network code. This could slow down the time the file existence finally shows up.
A solution would be not to use file shares but create a simple client/server architecture where the server returns the file existence from the local file system. That should really speed up item detection times.
My guess would be that if you tried to open the file even if File.Exists says it doesn't exist yet it should be opened correctly so you can use the server existence information. If that won't work you can simply add a download option to the server/client architecture.
Once I knew about the "File Not Found" cache, I was able to get around the problem by using a FileInfo object, which implements a Refresh() method. Your code could do this instead:
FileInfo testFile = new FileInfo("test/test2/myfile");
Console.WriteLine("Exists (should be false): " + testFile .Exists);
Directory.Move("test/subdir/test", "test/test2");
testFile.Refresh();
// the FileInfo object should now be refreshed, and a second call to Exists will return a valid value
if (testFile.Exists)
{
...
}

How do I check whether File.Delete() will succeed without trying it, in C#?

In C#, System.IO.File.Delete(filePath) will either delete the specified file, or raise an exception. If the current user doesn't have permission to delete the file, it'll raise an UnauthorizedAccessException.
Is there some way that I can tell ahead of time whether the delete is likely to throw an UnauthorizedAccessException or not (i.e. query the ACL to see whether the current thread's identity has permission to delete the specified file?)
I'm basically looking to do:
if (FileIsDeletableByCurrentUser(filePath)) {
/* remove supporting database records, etc. here */
File.Delete(filePath);
}
but I have no idea how to implement FileIsDeletableByCurrentUser().
The problem with implementing FileIsDeletableByCurrentUser is that it's not possible to do so. The reason is the file system is a constantly changing item.
In between any check you make to the file system and the next operation any number of events can and will happen. Including ...
Permissions on the file could change
The file could be deleted
The file could be locked by another user / process
The USB key the file is on could be removed
The best function you could write would most aptly be named FileWasDeletableByCurrentUser.
Have you tried System.IO.File.GetAccessControl(filename) it should return a FileSecurity with information about the permissions for that file.
Strictly speaking, an UnauthorizedAccessException means that the path is a directory, so you can use a System.IO.Path.GetFileName(path) type command and catch the argument exception.
But if you want a more holistic solution, use System.IO.File.GetAccessControl as mentioned by Dale Halliwell
As stated above.
Lookup the file permissions and compare with the user who is running the application.
You could always use this aproach as well
bool deletemyfile()
{
try
{
...delete my file
return true;
}
catch
{
return false;
}
}
if it returns false you know it failed if it returns true then.. it worked and file is gone. Not sure what you're after exactly but this was the best I could think of
Of course you can check ReadOnly flags using System.IO and probably ACL security on the file combined with the current user too, but like Mehrdad writes in his comment it would never be full-proof in all cases. So you would need exception handling for the exceptional case at all times (even if its just a top-level catch-all, that logs/shows an "unexpected problem" and kills your application).
You should get the access control list (ACL) of that file.
But this doesn't necessarily mean you could actually delete it because the readonly flag could still be set or another program has locked the file.
Seems like it would be easier to do things in the order of:
Get whatever information you need about the file in order to do the other parts (delete database data, etc)
Try to delete the file
If you successfully delete the file, then carry out the rest of the "cleanup" work. If you don't successfully delete it, return/handle the exception, etc.

Categories

Resources