none
Named Pipes Not Working When Logged In As a Standard User in Vista

    Question

  • Hi,

    I'm using named pipes to communicate between processes.
    The processes run at different integrity levels - LOW (ie toolbar) + MEDIUM (tray icon) + HIGH + SYSTEM (service).
    I'm defining a LOW integrity SACL when creating the named pipe.
    Things work great when logged in as an Administrator.
    When logged in as a Standard User however, only processes of identical integrity levels can communicate with one another because CreateFile fails to open an existing pipe with erorr ACCESS DENIED  (eg: MED to MED works, HIGH to HIGH works, ...)
    I need both READ and WRITE access to the pipe.

    What security attribute do I need to pass to CreateNamedPipe() to resolve this?
    Do I have to call SetSecurityInfo() after calling CreateNamedPipe() to resolve this somehow?

    I've spent a full day searching and trying various DACLs and SACLs and i've hit a brick wall.
    I'm crossing my fingers that someone can point me in the right direction.
    The problem is mentioned in this thread but no solution is offered:
    http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/ce75cdbd-a7c9-422f-b8e6-49ea4b4aa97f

    Relevant Code (error checking removed):
    =========

    LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)"
    ;
    PSECURITY_DESCRIPTOR securitydescriptor;
    ConvertStringSecurityDescriptorToSecurityDescriptorW(LOW_INTEGRITY_SDDL_SACL_W,SDDL_REVISION_1,&securitydescriptor,NULL);
    sa.nLength = sizeof
    (SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = securitydescriptor;
    sa.bInheritHandle = TRUE;
    
        HANDLE pipe = CreateNamedPipe(pipename,                    // pipe name 
    
                                      PIPE_ACCESS_DUPLEX,          // read/write access 
    
                                      PIPE_TYPE_MESSAGE |          // message type pipe 
    
                                      PIPE_READMODE_MESSAGE |      // message-read mode 
    
                                      PIPE_WAIT,                   // blocking mode 
    
                                      PIPE_UNLIMITED_INSTANCES,    // max. instances  
    
                                      PIPESERVER_OUTPUTBUFFERSIZE, // output buffer size 
    
                                      PIPESERVER_INPUTBUFFERSIZE,  // input buffer size 
    
                                      0,                           // client time-out 
    
                                      &sa);                        // security attribute
    
    
    
    <...create another LOW integrity psa...>

     HANDLE pipe = CreateFile(pipename, // pipe name
    GENERIC_READ | // read and write access
    GENERIC_WRITE,
    0, // no sharing
    psa, // default security attributes
    OPEN_EXISTING, // opens existing pipe
    0, // default attributes
    NULL); // no template file

    Thanks,
    Ignac

    Thursday, January 21, 2010 10:20 PM

Answers

  • I had a similar problem in .NET.  I assume you know C# since the link you provided was .NET.  This allowed Users to contact a named pipe server running in a service:

                pipeSecurity = new PipeSecurity();
                var usersPipeAccessRule = new PipeAccessRule(
                    new SecurityIdentifier( WellKnownSidType.BuiltinUsersSid, null ),
                    PipeAccessRights.ReadWrite | PipeAccessRights.Synchronize,
                    AccessControlType.Allow
                    );
                pipeSecurity.AddAccessRule(usersPipeAccessRule)
    
                    waitingPipe = new NamedPipeServerStream(
                        PipeName,
                        PipeDirection.InOut,
                        NamedPipeServerStream.MaxAllowedServerInstances,
                        PipeTransmissionMode.Message,
                        PipeOptions.Asynchronous,
                        ServicePipeProcessor.MaxBufferSize,
                        ServicePipeProcessor.MaxBufferSize,
                        pipeSecurity);


    I would assume you would want to use functions like CreateWellKnownSid and SetSecurityDescriptotDacl to do the same thing.

    • Marked as answer by Ignac Vucko Saturday, January 23, 2010 9:22 PM
    Friday, January 22, 2010 4:14 PM

All replies

  • I had a similar problem in .NET.  I assume you know C# since the link you provided was .NET.  This allowed Users to contact a named pipe server running in a service:

                pipeSecurity = new PipeSecurity();
                var usersPipeAccessRule = new PipeAccessRule(
                    new SecurityIdentifier( WellKnownSidType.BuiltinUsersSid, null ),
                    PipeAccessRights.ReadWrite | PipeAccessRights.Synchronize,
                    AccessControlType.Allow
                    );
                pipeSecurity.AddAccessRule(usersPipeAccessRule)
    
                    waitingPipe = new NamedPipeServerStream(
                        PipeName,
                        PipeDirection.InOut,
                        NamedPipeServerStream.MaxAllowedServerInstances,
                        PipeTransmissionMode.Message,
                        PipeOptions.Asynchronous,
                        ServicePipeProcessor.MaxBufferSize,
                        ServicePipeProcessor.MaxBufferSize,
                        pipeSecurity);


    I would assume you would want to use functions like CreateWellKnownSid and SetSecurityDescriptotDacl to do the same thing.

    • Marked as answer by Ignac Vucko Saturday, January 23, 2010 9:22 PM
    Friday, January 22, 2010 4:14 PM
  • Thanks for your reply Eric!

    Based on your reply, I had some success using a similar technique to the one you provided - I'm actually working in C++.
    Everything works great, except for one critical thing: communication with LOW integrity processes is not working.

    Before, I would explicitly specify a LOW integrity SACL during pipe creation (see orignal code paste above).
    Now, I'm instead setting a DACL to allow "EVERYONE' access during pipe creation (see code below).
    But, I can't seem to get it to recognize both the LOW integrity SACL and the EVERYONE DACL.
    Here's what I've tried:
    1) Call CreateNamedPipe() with a LOW integrity SACL and then try to add the Everyone DACL to the returned pipe handle
    2) Call CreateNamedPipe() with the Everyone DACL and then try to add the LOW integrity SACL to the returned pipe handle

    Neither seem to work...it seems that the security attribute MUST be specified upfront when the named pipe is created.

    Would you know how I can augment the code below to specify a LOW integrity SACL?

    - I've tried defining another EXPLICIT_ACCESS structure using SET_AUDIT_SUCCESS without luck
    - I've also tried using ConvertStringSecurityDescripterToSecurityDescriptorW() rather than simply doing a LocalAlloc of SECURITY_DESCRIPTOR, but that doesn't seem to work either
    - I'm sure I'm doing something wrong: this SACL/DACL stuff is mind boggling (at least for me).

    Again, I really appreciate your reply Eric,
    Ignac,

    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    PSID everyone_sid = NULL;
    AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyone_sid);
    
    EXPLICIT_ACCESS ea;
    ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
    ea.grfAccessPermissions = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;
    ea.grfAccessMode = SET_ACCESS;
    ea.grfInheritance = NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea.Trustee.ptstrName  = (LPWSTR)everyone_sid;
    
    PACL acl = NULL;
    SetEntriesInAcl(1, &ea, NULL, &acl);
    
    // HOW DO I ADD A LOW INTEGRITY SACL HERE THAT WILL NOT BE IGNORED ?

    PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH); InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE); SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = sd; sa.bInheritHandle = FALSE; CreateNamedPipe(...,&sa);


    Friday, January 22, 2010 5:35 PM
  • Congratulations on the progress.  It seems like it would work for low integrity since it's working for medium.  Have you tried this using SECURITY_NULL_SID_AUTHORITY for the SID_IDENTIFIER_AUTHORITY or adding a second entry in the ACL?
    Friday, January 22, 2010 6:12 PM
  • Here's what I have tried:
    hi Eric,

    - SECURITY_NULL_SID_AUTHORITY does not work (even non-low processes can't communicate)

    - tried merging in an SACL entry in the ACL using EXPLICIT_ACCESS and an access mode of SET_AUDIT_SUCCESS, but it doesn't seem to have any effect (or i'm doing something wrong). 

    - tried getting SECURITY_NULL_SID_AUTHORITY privilege, calling CreateNamedPipe with ACCESS_SYSTEM_SECURITY , and then adding a low integrity SACL after the call to CreateNamedPipe in two ways:

       a) Using SetSecurityInfo() => fails with ACCESS_DENIED
       b) Using SetEntriesInAcl() => no errors, but no effect

        Also, if I grab ACCESS_SYSTEM_SECURITY it makes things wors.
        Without it communication is only broken from LOW integrity to higher integriy but the reverse works.
        With it, communication is broken in both directions.

    Thanks,
    Ignac

    Friday, January 22, 2010 7:19 PM
  • I'm not sure if it would be any different, but try something like this:

    bool AddWellKnowSid(WELL_KNOWN_SID_TYPE WellKnownSidType, PACL pAcl)
    {
        bool returnValue = false;
        DWORD sidSize = SECURITY_MAX_SID_SIZE;
        PSID sid = LocalAlloc(LMEM_FIXED, sidSize);
    
        if(sid != NULL)
        {    
            if(CreateWellKnownSid(WellKnownSidType, NULL, sid, &sidSize))
            {
                if (AddAccessAllowedAce(pAcl, ACL_REVISION, 0xFFFF, sid))
                {
                    returnValue = true;
                }
            }
    
            LocalFree(sid);
        }
    
        return returnValue;
    }
    
    void Create()
    {
        DWORD cbAcl = 256;
        PACL pAcl = (ACL*)LocalAlloc(LPTR, cbAcl);
        if (pAcl == NULL)
            return;
    
        if (InitializeAcl(pAcl, cbAcl, ACL_REVISION))
        {
            if (AddWellKnowSid(WinAnonymousSid, pAcl) && 
                AddWellKnowSid(WinWorldSid, pAcl))
            {
                PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); 
                if (InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
                {
                    if (SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE))
                    {
                        SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), pSD, FALSE};
                        HANDLE hPipe = CreateNamedPipe(_T("\\\\.\\pipe\\namedpipe"),
                            PIPE_ACCESS_DUPLEX,
                            PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,
                            PIPE_UNLIMITED_INSTANCES, 512, 512, 0, &sa);
                        if (hPipe != INVALID_HANDLE_VALUE)
                        {
                            CloseHandle(hPipe);
                        }
                    }
                }
            }
        }
    
        LocalFree(pAcl);
    }
    
    Friday, January 22, 2010 9:21 PM
  • hi Eric,

    Thanks for the code.
    I tried it but it didn't work for me - a high integrity process could not talk to a medium integrity process if standard user.
    The DACL code I pasted earlier seems to solve that part for me...it's the low integrity SACL that I'm having issues with.
    Here's the code I try to use to set the SACL after creating the pipe handle....I cant figure out why SetSecurityInfo is returning ACCESS_DENIED.

    1. Use AdjustTokenPrivileges() to grab SE_SECURITY_NAME
    2. Specify the ACCESS_SYSTEM_SECURITY flag when calling CreateNamedPipe()
    3. Call the below function as follows: SetObjectToLowIntegrity(pipe,SE_KERNEL_OBJECT);


    bool SetObjectToLowIntegrity(HANDLE hObject,SE_OBJECT_TYPE type)
    {
      LPCWSTR LOW_INTEGRITY_SDDL_SACL_W = L"S:(ML;;NW;;;LW)";

      bool  bRet           = false;
      DWORD dwErr          = ERROR_SUCCESS;
      PACL  pSacl          = NULL;
      BOOL  fSaclPresent   = FALSE;
      BOOL  fSaclDefaulted = FALSE;
      PSECURITY_DESCRIPTOR pSD = NULL;
      
      if (ConvertStringSecurityDescriptorToSecurityDescriptorW(LOW_INTEGRITY_SDDL_SACL_W,SDDL_REVISION_1,&pSD,NULL))
      {
        if (GetSecurityDescriptorSacl(pSD,&fSaclPresent,&pSacl,&fSaclDefaulted))
        {
          dwErr = SetSecurityInfo(hObject,type,LABEL_SECURITY_INFORMATION,NULL,NULL,NULL,pSacl); // --------> FAILS WITH ACCESS_DENIED
          bRet = (ERROR_SUCCESS == dwErr);
        }
        LocalFree ( pSD );
      }
      
      return bRet;
    }
    Saturday, January 23, 2010 3:01 AM
  • hi Eric,

    Thanks for your suggestions.

    I FINALLY got it working.
    Man this stuff is as clear as mud.

    It seems that there is no way to modify the permissions of a named pipe after it has been created.
    I tried and it always either simply doesn't work or returns ACCESS_DENIED.
    So, I had to specify both the DACL and SACL up front when creating the pipe.

    The DACL code was pasted above in an earlier thread and the SACL can be created as follows:

    1. Create a new using InitializeAcl()
    2. Initialize a new SID specifying 'SECURITY_MANDATORY_LOW_RID'
    3. Call AddMandatoryAce() to add the SID to the ACL
    4. Call SetSecurityDescriptorSacl() to add the ACL to the security descriptor

    Cheers,
    Ignac
    • Proposed as answer by DesertSerenity Monday, February 25, 2013 8:54 AM
    • Unproposed as answer by DesertSerenity Monday, February 25, 2013 8:54 AM
    • Proposed as answer by Com3t Sunday, May 05, 2013 8:22 AM
    Saturday, January 23, 2010 9:22 PM
  • Congratulations!!  That's a pretty good puzzle you figured out.

    Saturday, January 23, 2010 9:49 PM
  • Do you have full sample code you can post. I am trying to accomplish this same thing and even with the code you have pasted here so far, I am having no luck.

    Thanks
    Saturday, February 27, 2010 7:05 AM
  • Hello

    You can check out the full named pipe sample in Microsoft All-In-One Code Framework:

    http://1code.codeplex.com

    See the C# version: CSNamedPipeServer, CSNamedPipeClient
    C++ version: CppNamedPipeServer, CppNamedPipeClient
    VB.NET version: VBNamedPipeServer, VBNamedPipeClient

    These samples have been fully tested in various Windows environments (e.g. high integrity level env, x64, pre-Vista env, etc)

    If you have any feedback of the samples, please feel free to email to codefxf@microsoft.com

     

     


    Regards,
    Jialiang Ge
    MSDN Subscriber Support in Forum
    If you have any feedback of our support, please contact msdnmg@microsoft.com.
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Proposed as answer by Com3t Sunday, May 05, 2013 8:23 AM
    • Unproposed as answer by Com3t Sunday, May 05, 2013 8:23 AM
    Thursday, March 25, 2010 5:35 AM
    Moderator
  • After 2 days of working on it, I've confirmed that Ignac approach does work correctly.  As he suggested, it seems to be impossible to set the SACL on the pipe after it has been created.

    I found this out when I tried to set PipeAccessRule and PipeAuditRule on a Pipe using the NamedPipeServerStream class in System.IO.Pipes.  I'm assuming that behind the scenes the NamedPipeServerStream constructor creates the pipe first and then tries setting the SACL/DACL.  As a result, the constructor fails telling you that you don't have sufficient privileges to perform the action (or something similar)

    I developed Ignac's solution using C#, but then once I'd successfully created the pipe with the correct security attributes, I passed the pipe handle into the NamedPipeServerStream constructor.  This seems to be working ok and now I can use NamedPipeServerStream to work with a pipe that can be accessed by Everyone and by low integrity processes as well.

    If someone has questions on this issue I'd be happy to help guide you.

     

    David K

    Wednesday, December 08, 2010 2:13 AM
  • Hello, I have checked the All-in-One examples.But they also giving the same problem as "Access Denied".In my case I have Piped Server in Windows Service and client is in .dll program.So is there any different method for PIPES when called from dll ?

    Please help as I am going nuts over here looking for this... I have tried all the possible solutions I have found but nothing is working

    Wednesday, December 28, 2011 7:37 AM
  • I know I'm coming 3 years late to this party, but I've stumbled into this mud.   Can you post a code sample representing your solution.

    rhfritz

    Wednesday, March 20, 2013 3:45 PM
  • I've searched the above link for both CppNPS & NPC and surmise that these examples have moved somewhere else.  Can you provide an alternate link?  Thanks.


    rhfritz

    I found it @ code.msdn.microsoft.com

    

    • Edited by rhfritz Wednesday, March 20, 2013 4:49 PM
    Wednesday, March 20, 2013 3:50 PM
  • http://code.msdn.microsoft.com/windowsdesktop/site/search?f%5B0%5D.Type=Technology&f%5B0%5D.Value=IPC%20and%20RPC&f%5B0%5D.Text=IPC%20and%20RPC


    Visual C++ MVP

    • Proposed as answer by Com3t Sunday, May 05, 2013 8:23 AM
    Wednesday, March 20, 2013 11:04 PM