Named Pipes from Windows Service to Client Application
Asked Answered
T

5

10

My story is that I am designing a new app which must communicate with a Windows service. After much research I have come to the conclusion that Named Pipes are the recommended method ( How do I send a string from one instance of my Delphi program to another? ) however, it appears that I can't use SendMessage or Named Pipes in Win7 due to security problems... the messages never reach outside the service to the application.

I am using the Russell Libby's named Pipe components, which work without a hitch between normal desktop apps, but the Windows service seems to be throwing a wrench in the solution. Further research tells me that it may be possible to open up security on both sides to let them communicate, however, my knowledge level on this is minimal at best, and I haven't been able to make heads or tails of the possible API calls.

Based on the Delphi component pipes.pas, what needs to be done to open up this baby so both sides can start talking? I'm sure the following two functions from the pipes.pas file identify the security attributes, is anyone able to help me out here?

Thanks!

procedure InitializeSecurity(var SA: TSecurityAttributes);
var
  sd: PSecurityDescriptor;
begin

  // Allocate memory for the security descriptor
  sd := AllocMem(SECURITY_DESCRIPTOR_MIN_LENGTH);

  // 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;
    end
    else
      // Failed to init the sec descriptor
      RaiseWindowsError;
  end
  else
    // Failed to init the sec descriptor
    RaiseWindowsError;

end;

procedure FinalizeSecurity(var SA: TSecurityAttributes);
begin

  // Release memory that was assigned to security descriptor
  if Assigned(SA.lpSecurityDescriptor) then
  begin
    // Reource protection
    try
      // Free memory
      FreeMem(SA.lpSecurityDescriptor);
    finally
      // Clear pointer
      SA.lpSecurityDescriptor := nil;
    end;
  end;

end;
Titustityus answered 13/7, 2011 at 19:21 Comment(9)
This code already assigns a null DACL to the security attributes. That means everybody can do anything to whatever object it's associated with. (Read the documentation for the functions used in your code.) Your problem lies elsewhere.Harbert
@Rob This works on local, but won't work over a network, for security reasons. Since Vista, anonymous access is at medium security level. So connection will fail, even in anonymous mode. This is the issue.Bluster
I already had the same problem. See this article. But I was not able to found out the right solution yet. I'll try the S:(ML;;NW;;;S-1-16-0) trick. ;)Bluster
Use TCP and you won't have security issuesAmimia
@Amimia But TCP is at least two times slower than named pipes, as far as I tested.Bluster
And TCP introduces lovely Windows Firewall prompts.Deweydewhirst
@Warren - I can't say if the local TCP interfer with the firewall, but it's really an overkill. For the network connections is TCP the easiest way (I'm not afraid to say necessary); pipes have almost no chance over network since Vista's paranoia.Whitworth
@A.Bouchez, the performance of TCP would rarely if ever be an issue with a local connection to a service. Quite frankly, performance only ever has to be "good enough", not the best, and what is "good enough" is defined by its use in each specific system - it is not a general concept.Amimia
@Warren P, only if you have a local firewall turned on, I never bother if I am running on a internal network that uses a router for internet access. And even so, there are ways of getting around this anyway.Amimia
M
8

Windows Vista, Seven and 2008 enforce a more secure use of named pipes, see for example http://blogs.technet.com/b/nettracer/archive/2010/07/23/why-does-anonymous-pipe-access-fail-on-windows-vista-2008-windows-7-or-windows-2008-r2.aspx

Mayapple answered 13/7, 2011 at 21:31 Comment(4)
Summary: Article says that unless you lower the integrity level of the pipe to untrusted, integrity level mismatches will prevent any access, regardless of what the classic access permission lists (ACL) are set to.Deweydewhirst
AFAIK it was just an issue with anonymous pipes.Mayapple
The implementation of that is posted here by A.Bouchez. I've been testing the same code for a long time (here is one of my steps) but it doesn't worked for me. Right, I've had a different scenario; I was trying to connect VMWare's machine to the physical one through the local network, but with no chance. Also; security attributes along with the descriptor has been set properly.Whitworth
I forgot to mention that I was unable to write to the pipe. Reading worked fine.Whitworth
M
2

When we migrated our product from Win 2K to Win7, we ran our Named Pipes quit working. After 2 weeks talking with MS (and $275), we discovered it was being caused by the Use Shared Folders file settings. Unchecking this feature allowed us to continue with pipes.

Mb answered 19/7, 2013 at 15:4 Comment(0)
B
1

I tried to implement this one:

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;

function ConvertSidToStringSidA(aSID: PSID; var aStr: PAnsiChar): BOOL; stdcall; external advapi32;
function ConvertStringSecurityDescriptorToSecurityDescriptorA(
  StringSecurityDescriptor: PAnsiChar; StringSDRevision: DWORD;
  SecurityDescriptor: pointer; SecurityDescriptorSize: Pointer): BOOL; stdcall; external advapi32;

const
  SDDL_REVISION_1 = 1;

procedure InitializeSecurity(var SA: TSecurityAttributes; var SD; Client: boolean);
var OK: boolean;
    Token: THandle;
    pSidOwner: PSID;
    pSid: PAnsiChar;
    SACL: AnsiString;
begin
  fillchar(SD,SECURITY_DESCRIPTOR_MIN_LENGTH,0);
  // Initialize the new security descriptor
  OK := false;
  if InitializeSecurityDescriptor(@SD, SECURITY_DESCRIPTOR_REVISION) then begin
    if Client or (OSVersionInfo.dwMajorVersion<6) then
      // before Vista: add a NULL descriptor ACL to the security descriptor
      OK := SetSecurityDescriptorDacl(@SD, true, nil, false)
     else begin
      // since Vista: need to specify special ACL
      if GetUserSid(pSidOwner,Token) then
      try
        if ConvertSidToStringSidA(pSidOwner,pSid) then
        try
          SACL := 'D:(A;;GA;;;'+pSID+')(A;;GWGR;;;AN)(A;;GWGR;;;WD)S:(ML;;NW;;;S-1-16-0)';
          OK := ConvertStringSecurityDescriptorToSecurityDescriptorA(
            pointer(SACL),SDDL_REVISION_1,@SD,nil);
        finally
          LocalFree(PtrUInt(pSid));
        end;
      finally
        FreeSid(pSidOwner);
        CloseHandle(Token);
      end;
    end;
  end;
  if OK then begin
    // Set up the security attributes structure
    SA.nLength := sizeof(TSecurityAttributes);
    SA.bInheritHandle := true;
    SA.lpSecurityDescriptor := @SD;
  end else
    fillchar(SA,sizeof(SA),0); // mark error: no security
end;

It seems to work on the server side (i.e. the security attributes are created as expected), and you will have to write the client side code, without forgetting to add the pipe name in SYSTEM\CurrentControlSet\Services\lanmanserver\parameters\NullSessionPipes registry key, as expected.

Bluster answered 14/7, 2011 at 9:46 Comment(3)
@Arnaud - wouldn't setting of the security attributes to nil and running of the application As administrator do the same (I mean full access) ? This hack is not well known and I bet there must be a clean way. Over the network fine, but on the same computer, only on different sessions ?Whitworth
@daemon Just setting nil will work on the same computer for the same session. Perhaps on sessions with the same user rights. But it won't work on Vista/Seven over a network.Bluster
@Arnaud - so does it mean that without this piece of code I'm not ensecured to establish a pipe from the service (session #0) to the client (session #1) ? With that ensecured I mean the worst case when you're not the administrator and you are running your applications (service and client part) without admin elevation ?Whitworth
H
0

I seem to remember that RemObjects has a named pipe client/server control in their package. Unless you are on a budget I would strongly recommend that you have a look at finished components for things like this. It is both time consuming and tricky to get right.

Alternatively, Justin Smyth has an article on named pipes right now. Check out his blog on the subject here: http://smythconsulting.blogspot.com/2011/07/smartmediaplayer-pipes-part4.html

Good luck!

Homburg answered 15/7, 2011 at 0:27 Comment(0)
Q
0

I had the same kind of problem and just solved it. For me the reason it didn't work was because Russels TPipe implementetion has a check on the threads ID's just before the Pipe gets created: if not(Sync.SyncBaseTID = FNotifyThread) then..

It turned out I was creating the TPipeServer at the wrong place in my service. (I overrided DoStart etc instead of using the event OnStart... don't do that!) I am now creating the TPipeServer instance in the same thread I later on activate it in.

Quentin answered 15/12, 2015 at 14:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.