I am trying to use NamedPipe to communicate between app and service in Win10. APP is developed with C#(UWP),running foreground as Pipe Client. And service is C++ running background as Pipe Server. Now the problem is that the APP couldn't connect the service. I know that MSFT doc said Pipes are only supported within an app-container. But I have tried the following cases: my uwp app VS C#(nonUWP) Server(not in an app-container); C++ Client VS C++ server(same code with service except running in foreground). Both cases works fine. So I think it may have something wrong with Security privilege. But I couldn't find somthing abnormal, Could someone help me out?
Client(UWP/C#):
_namedPipeClientHandle[index] = CreateFileW(@"\\.\pipe\starpipe",
DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
SHAREMODE.FILE_SHARE_READ | SHAREMODE.FILE_SHARE_WRITE,
0,
CREATIONDISPOSITION.OPEN_EXISTING,
FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
0);
if (_namedPipeClientHandle[index] != null && _namedPipeClientHandle[index].IsInvalid == false)
{
_namedPipeClientStream[index] = new FileStream(_namedPipeClientHandle[index], FileAccess.ReadWrite, 2048, true);
isConnected[index] = true;
break;
}
Server(C++):
EXPLICIT_ACCESS ea[2];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
if (!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) return false;
SecureZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
ea[0].grfAccessMode = SET_ACCESS;
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 = (LPTSTR)pEveryoneSID;
if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) return false;
ea[1].grfAccessPermissions = FILE_ALL_ACCESS | GENERIC_WRITE | GENERIC_READ;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance = NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR)pAdminSID;
DWORD dwRes = SetEntriesInAclW(2, ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRes) return false;
auto secDesc = std::vector<unsigned char>(SECURITY_DESCRIPTOR_MIN_LENGTH);
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)(&secDesc[0]);
if (nullptr == pSD) return false;
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) return false;
if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) return false;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
const char* pStrPipeName = "\\\\.\\pipe\\starpipe";
m_hPipe = CreateNamedPipeA(
pStrPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
2048,
2048,
0,
&sa);
if (m_hPipe == INVALID_HANDLE_VALUE) return false;
if (::ConnectNamedPipe(m_hPipe, NULL)) return true;
when named pipe created from UWP process (appcontainer) name must have form
"\\\\?\\pipe\\local\\SomeName"
system convert this name to
"\\\\?\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\SomeName"
so desktop application must create pipe with such name format for UWP be able open it.
but for this need know appcontainer-sid for uwp app and it session id. ok, appcontainer-sid is permanent for concrete UWP - never changed, but SessionId can be different (usually 1 but can be and another). also need set special security descriptor on pipe, which grant access for SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
. for example
"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)"
example of create such pipe in desktop
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
volatile UCHAR guz = 0;
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, HANDLE hProcess)
{
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, FALSE };
// SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)",
SDDL_REVISION_1, &sa.lpSecurityDescriptor, 0))
{
return GetLastError();
}
HANDLE hToken;
ULONG err = BOOL_TO_ERROR(OpenProcessToken(hProcess, TOKEN_QUERY, &hToken));
if (err == NOERROR)
{
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 128;
union {
PVOID buf;
PTOKEN_APPCONTAINER_INFORMATION AppConainer;
PWSTR sz;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenAppContainerSid, buf, cb, &rcb));
if (err == NOERROR)
{
ULONG SessionId;
err = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenSessionId, &SessionId, sizeof(SessionId), &rcb));
if (err == NOERROR)
{
PWSTR szSid;
err = BOOL_TO_ERROR(ConvertSidToStringSid(AppConainer->TokenAppContainer, &szSid));
if (err == NOERROR)
{
static const WCHAR fmt[] = L"\\\\?\\pipe\\Sessions\\%d\\AppContainerNamedObjects\\%s\\%s";
rcb = (1 + _scwprintf(fmt, SessionId, szSid, PipeName)) * sizeof(WCHAR);
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
_swprintf(sz, fmt, SessionId, szSid, PipeName);
HANDLE hPipe = CreateNamedPipeW(sz,
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 0, 0, 0, &sa);
if (hPipe == INVALID_HANDLE_VALUE)
{
err = GetLastError();
}
else
{
*PipeHandle = hPipe;
}
LocalFree(szSid);
}
}
break;
}
} while (err == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
}
LocalFree(sa.lpSecurityDescriptor);
return err;
}
ULONG CreatePipeforUWP(OUT PHANDLE PipeHandle, PCWSTR PipeName, ULONG dwProcessId)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, dwProcessId))
{
ULONG err = CreatePipeforUWP(PipeHandle, PipeName, hProcess);
CloseHandle(hProcess);
return err;
}
return GetLastError();
}
HANDLE hPipe;
if (CreatePipeforUWP(&hPipe, L"MyPipe", *) == NOERROR)
in client (UWP) app simply
CreateFileW(L"\\\\?\\pipe\\local\\MyPipe",
FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
but for this server need know UWP session id (or process id) or assume that UWP run in same session with server. in general this is not nice.
i be advice use RPC instead named pipes. here no any problems.
desktop need use RpcServerRegisterIf3
and set SecurityDescriptor for interface for let UWP access it. say "D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)"
. and this fine work with UWP, for example server code
ULONG InitRpcServer()
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
// generic all for SDDL_ALL_APP_PACKAGES + SDDL_EVERYONE
ULONG dwError = BOOL_TO_ERROR(ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)", SDDL_REVISION, &SecurityDescriptor, 0));
if (dwError == ERROR_SUCCESS)
{
dwError = RpcServerRegisterIf3(hello_v1_0_s_ifspec,
NULL, NULL, RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0x10000, 0, SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerUseProtseqEpW(
(RPC_WSTR)L"ncalrpc",
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
(RPC_WSTR)L"myname",
SecurityDescriptor);
if (dwError == RPC_S_OK)
{
dwError = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
}
}
LocalFree(SecurityDescriptor);
}
return dwError;
}
and client only need
RpcBindingFromStringBinding((RPC_WSTR)L"ncalrpc:[myname]", &IDL_handle)
According to this page
Pipes are only supported within an app-container; ie, from one UWP process to another UWP process that's part of the same app. Also, named pipes must use the syntax ".\pipe\LOCAL" for the pipe name.