Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MemoryMappedFileSecurity missing in .NET 6

I have a service that makes use of MemoryMappedFiles for interprocess communication. It has worked great for many years and was developed in .NET Framework 4.6.1. Now comes the time to port the code to .NET 6. I've gotten the bulk of it to work correctly except for one issue: the security ACL for the memory mapped file. That argument seems to have disappeared in .NET 6. Here is a snippet from the 4.6.1 Framework version

    fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
    FileSecurity fSec = File.GetAccessControl(FileName);
    fSec.AddAccessRule(new FileSystemAccessRule("everyone", FileSystemRights.FullControl, AccessControlType.Allow));
    File.SetAccessControl(FileName, fSec);
    if (fs.Length == 0)
        fs.SetLength(_SectionSize);
    long fLen = fs.Length;
    MemoryMappedFileSecurity security = new MemoryMappedFileSecurity();
    security.AddAccessRule(new AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, AccessControlType.Allow));
    //Name = @"Global\DCCCache";    // "Global\" when running as a service so session 0 stuff available to everyone
    _MMFHandle = MemoryMappedFile.CreateFromFile(fs, Name, _SectionSize, MemoryMappedFileAccess.ReadWrite, security, HandleInheritability.Inheritable, false);
    _VAHandle = _MMFHandle.CreateViewAccessor();

This all works and allows non-admin user processes access to the memory mapped file. .NET 6 drops the security argument from the .CreateFromFile method. As a result, only processes running with Administrator privileges have access to the memory mapped file. An "Access Denied" IO exception is thrown from the OpenExisting method of MemoryMappedFile for non-admin processes.

Is there a way to modify the security when I create a memory mapped file so non-admin processes have access?

like image 266
John Wolnisty Avatar asked Feb 01 '26 14:02

John Wolnisty


1 Answers

I did a workaround. Put together the call I needed to create the memory mapped file with the correct security and call it once in the service that owns it. I changed the mainline code to use OpenExisting after the call to the routine below so the remainder of the code can use the .NET 6 library as intended. Not ideal, but it fixed the issue.

using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace MMFService {
    internal class MMFNet6Shim : IDisposable {
        private bool win32Result = false;
        private int cbSid = SECURITY_MAX_SID_SIZE;
        private SECURITY_ATTRIBUTES securityAttributes = new SECURITY_ATTRIBUTES();
        private SafeMemoryMappedFileHandle hFile;

        private const int SDDL_REVISION_1 = 1;
        private const int SECURITY_MAX_SID_SIZE = 68;
        private const int PAGE_READWRITE = 0x04;
        private const int FILE_MAP_WRITE = 0X02;

        public MMFNet6Shim(FileStream fs, string DBName) {
            win32Result = ConvertStringSecurityDescriptorToSecurityDescriptor("D:(A;OICI;GA;;;WD)", SDDL_REVISION_1, out securityAttributes.lpSecurityDescriptor, IntPtr.Zero);
            if (!win32Result)
                throw new Exception("ConvertStringSecurityDescriptorToSecurityDescriptor", new Win32Exception(Marshal.GetLastWin32Error()));
            securityAttributes.nLength = Marshal.SizeOf(securityAttributes);
            securityAttributes.bInheritHandle = false;
            long fLen = fs.Length;
            hFile = CreateFileMapping(fs.SafeFileHandle, ref securityAttributes, PAGE_READWRITE, 0, Convert.ToInt32(fLen), DBName);
            if (hFile.IsInvalid) 
                throw new Exception("CreateFileMapping", new Win32Exception(Marshal.GetLastWin32Error()));
        }

        public void Dispose() {
            if(!hFile.IsInvalid)
                hFile.Close();
        }


        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor
        (
        [In] string StringSecurityDescriptor,
        [In] int StringSDRevision,
        [Out] out IntPtr SecurityDescriptor,
        [Out] IntPtr SecurityDescriptorSize
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LocalFree([In] IntPtr hMem);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern SafeMemoryMappedFileHandle CreateFileMapping(
        [In] SafeFileHandle hFile,
        [In][Optional] ref SECURITY_ATTRIBUTES lpAttributes,
        [In] int flProtect,
        [In] int dwMaximumSizeHigh,
        [In] int dwMaximumSizeLow,
        [In][Optional] string lpName
        );

    }
}
like image 143
John Wolnisty Avatar answered Feb 04 '26 07:02

John Wolnisty



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!