0

I am trying to communicate between client UWP C# app and server Win32 C++ app, both apps are in same package and I am using the Win32 app as desktop extension to provide additional functionality to UWP app.

From the docs it is clear that named pipe communication between UWP apps in same package but it isn't clear about UWP and Win32 app in same package.

The pipe created by UWP process with name \\\\.\\pipe\\Local\\PipeName is converted to \\\\.\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\PipeName. I can use this to communicate between UWP as server and Win32 as client. But I can't do the reverse even after I set up the ACLs as done in the official RPC sample. Instead of using custom capability I used DeriveAppContainerSidFromAppContainerName to to derive a SID from package family name.

My Win32 C++ server code looks like this:

SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
PSID everyoneSid = NULL;
PSID packageSid = NULL;
EXPLICIT_ACCESS ea[2] = {};
PACL acl = NULL;
SECURITY_DESCRIPTOR pipeSecurityDescriptor = {};

if (DeriveAppContainerSidFromAppContainerName(Package::Current().Id().FamilyName().c_str(), &packageSid) == S_OK &&
    // Get the SID that represents 'everyone' (this doesn't include AppContainers)
    AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid))
{
    // Now create the Access Control List (ACL) for the Security descriptor

    // Everyone GENERIC_ALL access
    ea[0].grfAccessMode = SET_ACCESS;
    ea[0].grfAccessPermissions = GENERIC_ALL;
    ea[0].grfInheritance = NO_INHERITANCE;
    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea[0].Trustee.ptstrName = static_cast<LPWSTR>(everyoneSid);

    // Package Family GENERIC_ALL access
    ea[1].grfAccessMode = SET_ACCESS;
    ea[1].grfAccessPermissions = GENERIC_ALL;
    ea[1].grfInheritance = NO_INHERITANCE;
    ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea[1].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
    ea[1].Trustee.ptstrName = static_cast<LPWSTR>(packageSid);

    if (SetEntriesInAcl(ARRAYSIZE(ea), ea, NULL, &acl) != ERROR_SUCCESS &&
        // Initialize an empty security descriptor
        InitializeSecurityDescriptor(&pipeSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) &&
        // Assign the ACL to the security descriptor
        SetSecurityDescriptorDacl(&pipeSecurityDescriptor, TRUE, acl, FALSE))
    {
        SECURITY_ATTRIBUTES pipeSecurityAttributes{ .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = &pipeSecurityDescriptor, .bInheritHandle = FALSE };
        HANDLE hPipe = CreateNamedPipe(L"\\\\.\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\PipeName}", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, &pipeSecurityAttributes);
        if (hPipe)
        {
           ConnectNamedPipe(hPipe, NULL);
           // Do something
        }
    }
}

if (everyoneSid) FreeSid(everyoneSid);
if (packageSid) FreeSid(packageSid);
if (acl) LocalFree(acl);

My UWP C# client code looks like this:

using (var client = new NamedPipeClientStream(".", "Local\\PipeName", PipeDirection.InOut, PipeOptions.Asynchronous))
        {
            await client.ConnectAsync();
            // Do something
        }

When I am trying to connect from client I am getting "Access to the path is denied." error.

Soumya Mahunt
  • 2,148
  • 12
  • 30
  • Even though the question looks similar to https://stackoverflow.com/questions/60431348/ipc-uwp-c-sharp-pipe-client-fails-on-connect-c-server, the answer to the question is not satisfactory and discussion on that question has long died down. – Soumya Mahunt Jan 05 '21 at 11:20
  • and what is not satisfactory here ? – RbMm Jan 05 '21 at 12:07
  • @RbMm the answer gives access to all the packages, but what I need is only access to UWP process in the same package. – Soumya Mahunt Jan 05 '21 at 12:35
  • but in what problem here ? set security descriptor for allow access only to this package instead "D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)". and this SD must be not only on pipe but for rpc set too via `RpcServerRegisterIf3`. also you can set custom security callback for check (`RPC_IF_CALLBACK_FN`) for filter your process – RbMm Jan 05 '21 at 13:05
  • IIRC named pipes were broken in C# due to the specific Win32 API they used. Try to P/Invoke to `CreateFile` directly then I think you can convert a `HANDLE` (`IntPtr`) to a .NET stream? – Peter Torr - MSFT Jan 05 '21 at 22:57
  • All the pipe features work *except* opening by name. – Peter Torr - MSFT Jan 05 '21 at 22:58
  • @PeterTorr-MSFT I think ever since dotnet team switched to uwp specific Win 32 APIs(**FromApp APIs) it works fine now. I can create named pipe server the same way in UWP C# app. Also, I tried using P/Invoke with `CreateFileFromApp` instead of `CreateFile` as in my knowledge `CreateFile` isn't available to UWP apps. In that case, I got a different error "Value out of range". – Soumya Mahunt Jan 06 '21 at 14:24
  • Would you like to try to use [LocalSettings](https://learn.microsoft.com/en-us/windows/uwp/design/app-settings/store-and-retrieve-app-data) to communication between UWP project and Win32 C++ project? – YanGu Jan 07 '21 at 07:26
  • @YanGu-MSFT No. – Soumya Mahunt Jan 07 '21 at 07:30
  • Would you like to try to use app services which is mentioned in the linked [document](https://learn.microsoft.com/en-us/windows/uwp/communication/interprocess-communication) to communicate between projects? – YanGu Jan 08 '21 at 07:18
  • @YanGu - MSFT the issue with app service is that it won't work if win32 process is elevated, which in some cases my app needs. – Soumya Mahunt Jan 08 '21 at 08:24
  • See https://stackoverflow.com/a/3214370/103167 which claims that the "Local" prefix cannot be used with named pipes. – Ben Voigt Mar 08 '22 at 16:50

1 Answers1

2

I was able to get a version of this working-- a C# UWP app pipe client successfully connecting to a non-UWP C++ pipe server. My setup is very similar to yours, but there are a few differences.

  1. I did not have much luck at all using the C# version of connecting with the client. I was successful with using CreateFileW. You can call this function inside of your C# by using an DllImport.
  2. My pipe names are slightly different. My non-UWP program is what is making my pipe server, so I think I do not need to use the "Local" sub-path. My server pipe path is: \\\\.\\pipe\\Pipe. My client pipe path is: \\.\pipe\\Pipe. I was able to get a connection with these names.
  3. In my own experience with using DeriveAppContainerSidFromAppContainerName, I could not seem to get the correct SID from it. I would get one back, but when I would compare it the SID from "CheckNetIsolation.exe LoopbackExempt -s" in Powershell, it was different. Not sure why that is, but I hard-coded the correct SID and that worked. Having the incorrect SID does result in an access denied error.
Tyler
  • 91
  • 4
  • 1
    "Local" evidently was intended to deal with multiple login session support in Terminal Services (which is active even on client versions of Windows as part of the "Fast User Switching" feature). However that doesn't work for named pipes: https://stackoverflow.com/a/3214370/103167 – Ben Voigt Mar 08 '22 at 16:49
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 08 '22 at 16:52