IP C: трубный клиент UWP C# не работает на сервере Connect C ++ - PullRequest
0 голосов
/ 27 февраля 2020

Я пытаюсь использовать NamedPipe для связи между приложением и службой в Win10. APP разработан с C# (UWP), работает на переднем плане как Pipe Client. А сервис C ++ работает в фоновом режиме как Pipe Server. Теперь проблема в том, что приложение не может подключиться к услуге. Я знаю, что MSFT c сказал, что каналы поддерживаются только в контейнере приложения. Но я пробовал следующие случаи: мой сервер uwp app VS C# (nonUWP) (не в контейнере приложения); C ++ Client VS C ++ сервер (тот же код со службой, кроме запуска на переднем плане). Оба случая работают нормально. Поэтому я думаю, что это может быть что-то не так с привилегией безопасности. Но я не смог найти что-то ненормальное, может ли кто-нибудь мне помочь?

Клиент (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;
                    }

Сервер ( 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;

Ответы [ 2 ]

1 голос
/ 28 февраля 2020

когда именованный канал, созданный из имени процесса UWP (appcontainer), должен иметь форму

"\\\\?\\pipe\\local\\SomeName"

, система преобразует это имя в

"\\\\?\\pipe\\Sessions\\<SessionId>\\AppContainerNamedObjects\\<AppContainerSid>\\SomeName"

, поэтому настольное приложение должно создать канал с таким форматом имени, чтобы UWP мог его открыть. но для этого нужно знать appcontainer-sid для приложения uwp и его идентификатор сессии. Хорошо, appcontainer-sid является постоянным для конкретного UWP - никогда не менялся, но SessionId может отличаться (обычно 1, но может быть и другой). Также необходимо установить специальный дескриптор безопасности для канала, который предоставляет доступ для SDDL_EVERYONE + SDDL_ALL_APP_PACKAGES + SDDL_ML_LOW. например

"D:(A;;GA;;;WD)(A;;GA;;;AC)S:(ML;;;;;LW)"

пример создания такого канала в настольном компьютере

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)

в клиентском (UWP) приложении просто

CreateFileW(L"\\\\?\\pipe\\local\\MyPipe", 
        FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

, но для этого сервера необходимо знать идентификатор сеанса UWP (или идентификатор процесса) или предполагать, что UWP работает в одном сеансе с сервером. в общем, это нехорошо.

Я бы посоветовал использовать RP C вместо именованных каналов. здесь никаких проблем.

для рабочего стола нужно использовать RpcServerRegisterIf3 и установить SecurityDescriptor для интерфейса, чтобы позволить UWP получить к нему доступ. скажем "D:P(A;;GA;;;WD)(A;;GA;;;AC)(A;;GA;;;S-1-15-2-2)S:(ML;;;;;LW)". и это прекрасно работает с UWP, например, с кодом сервера

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;
}

и клиенту нужно только

RpcBindingFromStringBinding((RPC_WSTR)L"ncalrpc:[myname]", &IDL_handle)
0 голосов
/ 27 февраля 2020

Согласно этой странице

Трубы поддерживаются только внутри app-контейнера; ie, от одного процесса UWP к другому процессу UWP, который является частью того же приложения. Кроме того, именованные каналы должны использовать синтаксис ". \ Pipe \ LOCAL" для имени канала.

...