How to share memory between services and user proc

2019-02-05 08:54发布

问题:

I have a set of Win32 applications that share information using a shared memory segment created with CreateFileMapping() and MapViewOfFile(). One of the applications is a system service; the remainder are started by the logged-in user. On Windows XP, there was no problem. We named our segments “Global\Something” and all was well.

The additional security in Vista (and assumedly Windows 7) appears to prevent this architecture from working. Normal users are not allowed to create (Win32 error 5) objects in the global namespace. The MSDN indicates that if the account has the “create global” privilege then all should be well, but this does not seem to be the case in practice. Also, Vista’s “integrity” features appear to prevent the “low integrity” user processes from accessing the “high integrity” service-created shared memory object. It looks like I should be able to fix this via some magical SetSecurityDescriptorSacl() incantation, but I’m having difficulty learning to speak sacl.

So the question is: What is the proper way of using a shared memory segment between services and normal user processes?

To preempt the easy answer of “just turn off UAC”, we’re in a fairly locked-down environment and that is not a possibility.

Edit: Both the service and the user process need read/write access to the segment.

回答1:

The simplest way would be to have your service create the shared memory and specify a DACL in CreateFileMapping that grants regular users read access to the shared memory.

Normal users don't have the create global privilege, but services can have this privilege. If you must have your users create the shared memory and then have the service probe it, you could have an IPC scheme where your user code sends a message to the service containing the file mapping handle, and the service would then call DuplicateHandle to get a reference to it. This would require your service to run with the debug privilege.

The simplest way to create a DACL is to use ConvertStringSecurityDescriptorToSecurityDescriptor, which takes a string in a format called SDDL specifying the ACL.

Writing Secure Code contains an excellent chapter on creating DACL's with SDDL.

// Error handling removed for brevity
SECURITY_ATTRIBUTES attributes;
ZeroMemory(&attributes, sizeof(attributes));
attributes.nLength = sizeof(attributes);
ConvertStringSecurityDescriptorToSecurityDescriptor(
         L"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GR;;;IU)",
         SDDL_REVISION_1,
         &attributes.lpSecurityDescriptor,
         NULL);

CreateFileMapping(INVALID_HANDLE_VALUE, &attributes,
              PAGE_READWRITE, sizeHigh, sizeLow, L"Global\\MyObject");

LocalFree(attributes.lpSecurityDescriptor);

"D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GR;;;IU)" specifies the DACL. D:P means this is a DACL (instead of a SACL . . . you'd rarely use SACL's) followed by several ACE strings which control who gets access. Each one is A (allow) and allows for object and contains inheritance (OICI). The first to grant all access (GA - grant all) to System (SY) and Administrators (BA, built-in administratos). The last grants read (GR) to interactive users (IU), which are users actually logged on to a session.

Once this is done, normal users should be able to call OpenFileMapping to get a handle to the shared mapping, and be able to map it into their process. Since normal users have limited rights on the object, they'll have to be sure to open it and map it for read-access only.

If users need write-acccess, you'd replace GR with GWGR. Note that this isn't secure - a limited user would then be able to modify the shared memory while your service is reading and trying to parse information, resulting in a crash of your service.