This feature is a flat C API that takes a list of Package Family Names (PFNs) and access masks, and returns a security descriptor
Named objects (mutexes, events, waitable timers) must be ACLed during creation in order to be shared with packaged applications.
Currently, our guidance for this scenario is to write a large amount of complex, cumbersome, and largely boilerplate code. (See here for a taste).
If we are to meet developers where they are, we need to reduce the friction of Win32 and UWP interop, and make it as easy as possible to share named objects.
Add a flat C API that takes an array of PFNs and access masks, and produces the needed SECURITY_ATTRIBUTES
object.
#include <wil/resource.h>
wil::unique_event CreateShareableEvent(PCWSTR name)
{
wil::unique_hlocal_security_descriptor sd;
AppContainerNameAndAccess access[1] =
{{L"Contoso.Test.App_12345678",
EVENT_MODIFY_STATE | SYNCHRONIZE}};
THROW_IF_FAILED(GetSecurityDescriptorForAppContainerNames(
1, access, nullptr, EVENT_MODIFY_STATE | SYNCHRONIZE, &sd, nullptr));
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = sd.get();
sa.bInheritHandle = FALSE;
wil::unique_event result;
result.create(wil::EventOptions::None, name, &sa);
// aka, ::CreateEvent(&sa, FALSE, FALSE, name);
return result;
}
EventWaitHandle CreateShareableEvent(String name)
{
var access = new AppContainerNameAndAccess[]
{
new AppContainerNameAndAccess("Contoso.Test.App_12345678",
(uint)(EventWaitHandleRights.Modify |
EventWaitHandleRights.Synchronize));
};
string sddl = SecurityDescriptorHelpers.GetSddlForAppContainerNames(
access, "",
(uint)(EventWaitHandleRights.Modify |
EventWaitHandleRights.Synchronize));
var security = new EventWaitHandleSecurity();
security.SetSecurityDescriptorSddlForm(sddl);
Boolean created;
return new EventWaitHandle(false,
EventResetMode.AutoReset, name, out created, security);
}
struct AppContainerNameAndAccess
{
PCWSTR appContainerName;
UINT32 accessMask;
};
STDAPI GetSecurityDescriptorForAppContainerNames(
UINT32 accessRequestCount,
_In_reads_(accessRequestCount)
const AppContainerNameAndAccess* accessRequests,
_In_opt_ PSID principal,
UINT32 principalAccessMask,
_Outptr_ PSECURITY_DESCRIPTOR* securityDescriptor,
_Out_opt_ UINT32* securityDescriptorSize
)
If the principal parameter is null, the principal of the current thread is used.
If the function succeds, the SECURITY_DESCRIPTOR
returned in securityDescriptor
must be freed by calling LocalFree.
namespace Microsoft.Security.AccessControl
{
runtimeclass AppContainerNameAndAccess
{
AppContainerNameAndAccess()
AppContainerNameAndAccess(
String appContainerNameArg,
UInt32 accessMaskArg)
String appContainerName;
UInt32 accessMask;
}
static runtimeclass SecurityDescriptorHelpers
{
static String GetSddlForAppContainerNames(
AppContainerNameAndAccess[] accessRequests,
String principalStringSid,
UInt32 principalAccessMask
)
static UInt8[] GetSecurityDescriptorBytesFromAppContainerNames(
AppContainerNameAndAccess[] accessRequests,
String principalStringSid,
UInt32 principalAccessMask
)
}
}
The principalStringSid parameter is the security identifier (SID), in string format, of the principal (See also: ConvertSidToStringSid). If this parameter is empty, the principal of the current thread is used.
Some alternative designs were considered.
- Single method, takes a list of permissions corresponding to the list of PFNs. (Current choice)
- Pros
- Handles both complicated case and simple case in a single call.
- API is straightforward, no extra/optional/multimode parameters.
- Only a single method to search/remember/code.
- Cons
- Simple case is not "optimally" simple - requires filling array with a single value.
- Pros
- Single method, takes both a single permission as well as an optional array of permissions. The single permission is ignored if the array is non-null.
- Pros
- Handles both complicated case and simple case in a single call.
- Only a single method to search/remember/code.
- Provides a way to assign a single permission to all PFNs without filling an array.
- Cons
- Slightly more complicated API. Two parameters are conditionally used for the same purpose.
- Pros
- Two methods, one that takes a list of permissions, and one that takes a single permission.
- Pros
- Each API is straightforward, no extra/optional/multimode parameters.
- Dedicated APIs for assigning a single permission vs different permissions.
- Cons
- How to meaningfully name these two methods without unintentionally confusing or obfuscating?
- Increased mental model tax - now have to search/remember/read two different, but similar APIs.
- Pros