#1 2010-07-04 14:41:09

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,206
Website

Named Pipe, Vista, Seven and Service

If you want some local communicate between a service software and a front-end GUI application, named pipes are a viable mechanism for this communication. It worked fine until Windows XP, then came Vista, Seven, and the UAC...

By default, when you create a named pipe under Windows, it is accessible only by other applications running under the same user account. Under XP, it worked if the two users were part of the same group, or at least did share some security policy attributes. But, for security reasons, if the background service runs as local system while the application with the user interface runs as the logged on user, you'll receive a ERROR_ACCESS_DENIED flag under Vista and Seven.

If you are willing to open up access to your pipe to all clients, you'd have to explicitly create a TSecurityAttributes record instance, initialized with full user rights:

procedure InitializeSecurity(var SA: TSecurityAttributes; var SD);
begin
  fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
  // Initialize the new security descriptor
  if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) then begin
     // Add a NULL descriptor ACL to the security descriptor
     if SetSecurityDescriptorDacl(@SD, true, nil, false) then begin
        // Set up the security attributes structure
        SA.nLength := sizeof(TSecurityAttributes);
        SA.lpSecurityDescriptor := @SD;
        SA.bInheritHandle := true;
        exit; // mark OK
     end;
  end;
  fillchar(SA,sizeof(SA),0); // mark error: no security
end;

This works fine, but it doesn't allow you remote access to a service from another computer.

I've tried to implement what Microsoft told officially on http://support.microsoft.com/kb/813414, but I didn't succeed as expected. The trick is to allow access as anonymous user to the pipe...

Here is the code I wrote from original c sample:

function GetUserSid(var SID: PSID; var Token: THandle): boolean;
var TokenUserSize: DWORD;
    TokenUserP: PSIDAndAttributes;
begin
  result := false;
  if not OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, Token) then
    if (GetLastError <> ERROR_NO_TOKEN) or
       not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, Token) then
      Exit;
  TokenUserP := nil;
  TokenUserSize := 0;
  try
    if not GetTokenInformation(Token, TokenUser, nil, 0, TokenUserSize) and
       (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
      Exit;
    TokenUserP := AllocMem(TokenUserSize);
    if not GetTokenInformation(Token, TokenUser, TokenUserP,
       TokenUserSize, TokenUserSize) then
      Exit;
    SID := TokenUserP^.Sid;
    result := true;
  finally
    FreeMem(TokenUserP);
  end;
end;

{$ALIGN ON}
type
    ACE_HEADER = record
      AceType: BYTE;
      AceFlags: BYTE;
      AceSize: WORD;
    end;
    ACCESS_ALLOWED_ACE = record
      Header: ACE_HEADER;
      Mask: ACCESS_MASK;
      SidStart: DWORD;
    end;
{$A8}

procedure InitializeSecurity(var SA: TSecurityAttributes; var SD);
const
  SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
  SECURITY_ANONYMOUS_LOGON_RID = ($00000007);
  ACL_REVISION = 2;
var pSidAnonymous, pSidOwner: PSID;
    dwAclSize: integer;
    ACLP: PACL;
    Token: THandle;
begin
  fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
  // Initialize the new security descriptor
  if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) and
      GetUserSid(pSidOwner,Token) then begin
    AllocateAndInitializeSid(SECURITY_NT_AUTHORITY,1,
      SECURITY_ANONYMOUS_LOGON_RID,0,0,0,0,0,0,0,pSidAnonymous);
    try
      dwAclSize := sizeof(TACL) +
        2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
        GetLengthSid(pSidAnonymous) + GetLengthSid(pSidOwner) ;
      ACLP := AllocMem(dwAclSize);
      try
        InitializeAcl(ACLP^,dwAclSize,ACL_REVISION);
        if not AddAccessAllowedAce(ACLP^,ACL_REVISION,
           GENERIC_ALL,pSidOwner) then
          exit;
        if not AddAccessAllowedAce(ACLP^,ACL_REVISION,
           GENERIC_READ or GENERIC_WRITE,pSidAnonymous) then
          exit;
        if SetSecurityDescriptorDacl(@SD,true,ACLP,false) then begin
           // Set up the security attributes structure
           SA.nLength := sizeof(TSecurityAttributes);
           SA.lpSecurityDescriptor := @SD;
           SA.bInheritHandle := true;
           exit; // mark OK
        end;
      finally
        FreeMem(ACLP);
      end;
    finally
      FreeSid(pSidAnonymous);
      CloseHandle(Token);
    end;
  end;
  fillchar(SA,sizeof(SA),0); // mark error: no security
end;

But it didn't work as expected. Of course, I added the name of the pipe to the SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes registry entry, but it didn't work...

Whole source code is available from our source code repository, file SQLite3\SQlite3Commons.pas

Of course, we have implemented HTTP/1.1 connection, which works very well remotely over a network, so if you need networking, consider using HTTP... but it could be an interesting challenge to make named pipes work over the network!

Offline

#2 2011-03-02 08:08:32

Fess
Member
Registered: 2011-02-28
Posts: 1

Re: Named Pipe, Vista, Seven and Service

Look here: http://msdn.microsoft.com/en-us/library … s.85).aspx
and
At the client too should be filled SID by a procedure call CreateFile smile

Offline

#3 2012-02-21 12:46:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,206
Website

Re: Named Pipe, Vista, Seven and Service

AFAIK this is what we did - without success...

Offline

Board footer

Powered by FluxBB