4

My scenario is the following: the process that creates the named pipe object with CreateNamedPipe() has administrator privileges, but the client process "connecting" to it with CreateFile() does not. Passing a NULL as the last argument to CreateNamedPipe() appears to default to admin-only access rights.

As a hack, I've tried do a server-side ImpersonateLoggedOnUser()/RevertToSelf() method for the duration of the pipe related code, but it fails. Seems to me like the best thing to do here is to actually set a proper SECURITY_ATTRIBUTES struct to the last parameter of CreateNamedPipe(), but I'm having trouble figuring out how to do that.

The MSDN example has an example pertaining to registry key manipulation, but I lack the expertise to adapt that to my purposes.

This is what I've tried:

if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
    SECURITY_WORLD_RID,
    0, 0, 0, 0, 0, 0, 0,
    &pEveryoneSID))
{
    _tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
    ret_val = 0;
    goto Cleanup;
}

// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;
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;

// there's another ACE for administrators in between, but is of no relevance here

dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);

// Initialize a security descriptor.  
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
    SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
    _tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
    ret_val = 0;
    goto Cleanup;
}

if (!InitializeSecurityDescriptor(pSD,
    SECURITY_DESCRIPTOR_REVISION))
{
    _tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
        GetLastError());
    ret_val = 0;
    goto Cleanup;
}

// Add the ACL to the security descriptor. 
if (!SetSecurityDescriptorDacl(pSD,
    TRUE,     // bDaclPresent flag   
    pACL,
    FALSE))   // not a default DACL 
{
    _tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
        GetLastError());
    ret_val = 0;
    goto Cleanup;
}

    // Initialize a security attributes structure.
*sa = new SECURITY_ATTRIBUTES;

(*sa)->nLength = sizeof(SECURITY_ATTRIBUTES);
(*sa)->lpSecurityDescriptor = pSD;
(*sa)->bInheritHandle = FALSE;

Outcome is that client-side gets the error 0x5 (access denied) on CreateFile(). What is wrong here?

ehoopz
  • 139
  • 3
  • 10
  • [*"If `lpSecurityAttributes` is NULL, the named pipe gets a default security descriptor and the handle cannot be inherited. The ACLs in the default security descriptor for a named pipe grant full control to the LocalSystem account, administrators, and the creator owner. They also grant read access to members of the Everyone group and the anonymous account."*](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150.aspx) – Cody Gray - on strike Jul 16 '16 at 16:52
  • your error in line SetEntriesInAcl(2, ea, NULL, &pACL); when need SetEntriesInAcl(1, ea, NULL, &pACL); - you really init and use only 1 entry – RbMm Jul 16 '16 at 18:43
  • @RbMm: if you read the comments, he's just left that bit of the code out. Ehoopz, please try to avoid doing that, as it often causes confusion. Also, you'd be surprised how often the actual problem turns out to be in a bit of the code you thought was irrelevant. :-) – Harry Johnston Jul 17 '16 at 02:48
  • Yeah, sorry @RbBm, should've made the comment more conspicuous, or indeed just left it in. – ehoopz Jul 17 '16 at 07:51

3 Answers3

1

You can set the descriptor's DACL to NULL instead to allow anyone to access the pipe:

pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!pSD)
{
    ...
}

if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
    ...
}

if (!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))
{
    ...
}

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;

... = CreateNamedPipe(..., &sa);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Depending on what the pipe is being used for, this might not be safe. It would allow any process on the system to create a new instance of the named pipe, potentially intercepting data meant for the named pipe server or (in some cases) even impersonating the client. – Harry Johnston Jul 17 '16 at 01:55
  • This allows any client process to connect to the named pipe server without being rejected by security permissions. It does not allow a named pipe client to intercept data between another client and the server, or to impersonate another client. Besides, only the server can impersonate a client (see [`ImpersonateNamedPipeClient()`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378618.aspx)). – Remy Lebeau Jul 17 '16 at 02:02
  • 1
    Theoretically, at least, it allows any process to become a server for the same named pipe, because it grants FILE_CREATE_PIPE_INSTANCE. Other mechanisms might come into play, though, I'd have to experiment. Even if that is so, though, I'd personally prefer to use an ACL that explicitly requires admin privilege to create another instance, just in case. – Harry Johnston Jul 17 '16 at 02:35
  • WORKS! Cool. Granting full access permissions to everybody seems kind of extreme though. Also, I just realized that in my particular scenario, the roles of the two processes could be swapped rather easily; i.e. have the unprivileged process create the named pipe and not the other way around. I'll try it and see how that goes. – ehoopz Jul 17 '16 at 08:11
  • Despite opting for a different solution in the end (the client/server swap I mentioned in the previous comment), this answers the original question, so I accepted it. – ehoopz Jul 17 '16 at 08:52
1

Here's your problem:

ea[0].grfAccessPermissions = STANDARD_RIGHTS_ALL;

STANDARD_RIGHTS_ALL is not all rights, only all standard rights, i.e., delete, read control, synchronize, write DAC, and write owner. In particular it does not grant FILE_READ_DATA or FILE_WRITE_DATA, which a client needs in order to read and/or write data to the pipe.

I'd recommend

ea[0].grfAccessPermissions = GENERIC_READ | FILE_WRITE_DATA;

and have the client request the same access rights when opening the pipe. (Obviously, you can leave out the FILE_WRITE_DATA right if this is an outbound pipe, although in that case the default permissions should be OK.)

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • Thank you for the note on `STANDARD_RIGHTS_ALL`. Changing the `grfAccessPermissions` didn't seem to have an effect though, still getting `ERROR_ACCESS_DENIED`. – ehoopz Jul 17 '16 at 08:03
  • Worked for me, in my test application based on the code you posted. Probably your client code was asking for the wrong permissions when it opened the pipe, they have to match with what is set on the server. – Harry Johnston Jul 17 '16 at 22:37
-1

error in line SetEntriesInAcl(2, ea, NULL, &pACL); when need SetEntriesInAcl(1, ea, NULL, &pACL); - you really init and use only 1 entry. and not check result returned by SetEntriesInAcl. the next code will be work correct:

        EXPLICIT_ACCESS ea = {
            STANDARD_RIGHTS_ALL,SET_ACCESS, NO_INHERITANCE, { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, (LPTSTR)pEveryoneSID }
        };
        PACL pACL;
        if (SetEntriesInAcl(1, &ea, NULL, &pACL) == ERROR_SUCCESS)
        {
        }

here also can be access denied by Integrity Level. need check os version, and if vista+ set LowLabel in security descriptor. and can use 0 DACL. ( by default system assume MediumLabelSid if it not explicitly set, as result LowIntegrity clients fail open pipe, but for usual not admin clients solution by @Remy Lebeau is enough )

PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)alloca(SECURITY_DESCRIPTOR_MIN_LENGTH);

BOOL fOk = FALSE;

if (
    InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) 
    && 
    SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, 0, 0)
    )
{
    RTL_OSVERSIONINFOW rov = { sizeof (rov)};

    if (0 <= RtlGetVersion(&rov))
    {
        if (rov.dwMajorVersion < 6)
        {
            fOk = TRUE;
        }
        else
        {
            PSID LowLabelSid = (PSID)alloca(64);
            ULONG cbSid = 64;
            if (CreateWellKnownSid(::WinLowLabelSid, 0, LowLabelSid, &cbSid))
            {
                ::PACL LowLabelAcl = (::PACL)alloca(64+cbSid);
                InitializeAcl(LowLabelAcl, 64+cbSid, ACL_REVISION);
                if (AddMandatoryAce(LowLabelAcl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, LowLabelSid))
                {
                    fOk = SetSecurityDescriptorSacl(pSecurityDescriptor, TRUE, LowLabelAcl, FALSE);
                }
            }
        }
    }
}
RbMm
  • 31,280
  • 3
  • 35
  • 56