C++:允许用户进程写入LOCAL_SYSTEM命名管道 - 自定义安全描述符

6
我有一个以LocalSystem运行的服务,它在已登录用户的会话中创建进程。然后服务创建了一个命名管道,客户端连接到该管道进行读写操作。根据https://msdn.microsoft.com/en-us/library/aa365600%28v=vs.85%29.aspx,客户端只能从管道中读取(不是管理员,也不是创建者,也不是LocalSystem)。
我创建了一个安全描述符来授予用户读写访问权限,但这并没有起作用。所以我尝试给Everyone组赋予读写访问权限,但这也不起作用。我的客户端返回的错误代码始终为ACCESS_DENIED(5)。
我很高兴知道我做错了什么。
编辑:如果我不创建自定义安全描述符,只使用GENERIC_READ打开管道,那么它可以工作(但只能读取)。
编辑2:我想学习如何正确操作。我仍然希望只有已登录用户才能写入,而不是每个人(这只是用于测试)。

服务代码(我已注释掉获取用户SID的代码):

PSID EveryoneSID                      = nullptr;
        SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
        if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                                 SECURITY_WORLD_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 &EveryoneSID))
        {
            throw(std::runtime_error("Failed to initialize group sid: " + std::to_string(GetLastError())));
        }

        //TOKEN_USER* tokeninfo     = nullptr;
        //DWORD tokeninfolen        = 0;
        //DWORD outlen              = 0;
        //// Query token info size
        //if(!GetTokenInformation(UserToken,
        //                  TokenUser,
        //                  tokeninfo,
        //                  tokeninfolen,
        //                  &outlen))
        //{
        //  throw(std::runtime_error("Failed to obtain user token size: " + std::to_string(GetLastError())));
        //}
        //// Allocate enough space to hold token user information
        //tokeninfo    = (TOKEN_USER*) LocalAlloc(LPTR, outlen);
        //tokeninfolen = outlen;

        //// Get SID from user token
        //if(!GetTokenInformation(UserToken,
        //                  TokenUser,
        //                  tokeninfo,
        //                  tokeninfolen,
        //                  &outlen))
        //{
        //  throw(std::runtime_error("Failed to obtain user token info: " + std::to_string(GetLastError())));
        //}

        //auto UserSID = tokeninfo->User.Sid;
        //LocalFree(tokeninfo);

        SECURITY_ATTRIBUTES sa = {0};
        ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = FALSE;

        // Set up ACE
        EXPLICIT_ACCESS ace      = {0};
        ace.grfAccessMode        = SET_ACCESS;
        ace.grfAccessPermissions = PIPE_ACCESS_DUPLEX;  // GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE
        ace.grfInheritance       = NO_INHERITANCE;
        ace.Trustee.TrusteeForm  = TRUSTEE_IS_SID;
        ace.Trustee.TrusteeType  = TRUSTEE_IS_WELL_KNOWN_GROUP;
        ace.Trustee.ptstrName    = (LPTSTR) EveryoneSID;

        PACL acl = nullptr;
        if(ERROR_SUCCESS != SetEntriesInAcl(1, &ace, nullptr, &acl))
            throw(std::runtime_error("Failed to set acl entries: " + std::to_string(GetLastError())));

        // Create security descriptor.
        auto sd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
        if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
            throw(std::runtime_error("Failed to initialize security descriptor: " + std::to_string(GetLastError())));
        if(!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE))
            throw(std::runtime_error("Failed to set DACL: " + std::to_string(GetLastError())));

        // Set security descriptor in security attributes
        sa.lpSecurityDescriptor = sd;

        // Create a named pipe to which the user-session application
        // connects.
        auto pipe = CreateNamedPipe(LOCAL_PIPE_NAME,
                         PIPE_ACCESS_DUPLEX,
                         PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS | PIPE_WAIT,
                         1,
                         256,
                         256,
                         NULL,
                         &sa);
        if(pipe == INVALID_HANDLE_VALUE)
            throw std::runtime_error("Failed to create named pipe");

        LocalFree(acl);
        LocalFree(sd);

用户进程代码:

auto pipe = CreateFile(LOCAL_PIPE_NAME,
                           GENERIC_READ | GENERIC_WRITE,    // read access 
                            0,              // no sharing 
                            NULL,           // default security attributes
                            OPEN_EXISTING,  // opens existing pipe 
                            0,              // default attributes 
                            NULL);          // no template file 
    if (pipe == INVALID_HANDLE_VALUE)
        throw(std::runtime_error("Failed to open local pipe: " + std::to_string(GetLastError())));

@HarryJohnston:这是答案的主要部分。请创建它,以便我可以接受它作为答案。 - Juarrow
2个回答

4

这是第一个问题:

    ace.grfAccessPermissions = PIPE_ACCESS_DUPLEX;
PIPE_ACCESS_DUPLEX常量仅用作CreateNamedPipe()的参数,它不是有效的访问权限。(巧合的是,它等于FILE_READ_DATA|FILE_WRITE_DATA,但这些访问权限本身并不能让您连接到管道。)
根据命名管道安全性和访问权限,双工管道的服务器端被分配以下访问权限,这意味着它们也足以打开客户端:
    ace.grfAccessPermissions = FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE;

然而,FILE_GENERIC_WRITE 权限过于宽泛,不适合授予客户端;特别是,它允许客户端创建一个新的管道服务器端实例。这很可能是不可取的。相反,在双向管道中,您应该使用:
    ace.grfAccessPermissions = FILE_GENERIC_READ | FILE_WRITE_DATA;

当然,在打开客户端时请求的访问必须保持一致:
auto pipe = CreateFile(LOCAL_PIPE_NAME,
                       GENERIC_READ | FILE_WRITE_DATA,    // read-write access 
                       0,              // no sharing 
                       NULL,           // default security attributes
                       OPEN_EXISTING,  // opens existing pipe 
                       0,              // default attributes 
                       NULL);          // no template file 

细节

实验表明,在Windows 7 SP1 x64上,为了连接到一个管道(即使在调用CreateFile时请求不访问),您必须拥有READ_ATTRIBUTESSYNCHRONIZE权限。请注意,FILE_GENERIC_READ常量包含这两个权限。

要从管道读取数据,您必须具有(并请求)FILE_READ_DATA权限。(这已经包含在FILE_GENERIC_READ中。)

要向管道写入数据,您必须具有(并请求)FILE_WRITE_DATA权限。


0

有一个更简单的选项 - 将安全描述符的dacl设置为NULL:

SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);

SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;

这不是我想要做的。我只想允许特定的用户。添加所有人只是为了测试。而且我想学习并知道如何正确地做到这一点。 - Juarrow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接