【问题标题】:Named Pipes from Windows Service to Client Application从 Windows 服务到客户端应用程序的命名管道
【发布时间】:2011-10-04 18:59:42
【问题描述】:

我的故事是我正在设计一个必须与 Windows 服务通信的新应用程序。经过大量研究,我得出的结论是命名管道是推荐的方法(How do I send a string from one instance of my Delphi program to another?)但是,由于安全问题,我似乎无法在 Win7 中使用 SendMessage 或命名管道......消息永远不会到达外部为应用程序提供服务。

我正在使用 Russell Libby 的命名管道组件,这些组件可以在普通桌面应用程序之间顺利运行,但 Windows 服务似乎在解决方案中遇到了麻烦。进一步的研究告诉我,有可能在双方都开放安全性以让他们进行通信,但是,我对此的知识水平充其量是最低限度的,而且我无法对可能的 API 调用做出正面或反面.

基于Delphi组件pipes.pas,需要做什么才能打开这个宝贝让双方开始对话?我确定 pipes.pas 文件中的以下两个函数标识了安全属性,有人可以在这里帮助我吗?

谢谢!

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;

【问题讨论】:

  • 此代码已将空 DACL 分配给安全属性。这意味着 everybody 可以对与之关联的任何对象执行anything。 (阅读代码中使用的函数的文档。)您的问题出在其他地方。
  • @Rob 出于安全原因,这适用于本地,但不适用于网络。自 Vista 以来,匿名访问处于 中等 安全级别。因此,即使在匿名模式下,连接也会失败。这就是问题所在。
  • 我已经遇到了同样的问题。见this article。但我还没有找到正确的解决方案。我会尝试S:(ML;;NW;;;S-1-16-0) 技巧。 ;)
  • 使用 TCP 不会有安全问题
  • @Warren P,仅当您打开了本地防火墙时,如果我在使用路由器访问 Internet 的内部网络上运行,我从不打扰。即便如此,还是有办法解决这个问题的。

标签: delphi windows-services desktop-application c++builder named-pipes


【解决方案1】:

【讨论】:

  • 总结:文章说,除非您将管道的完整性级别降低到不受信任,否则完整性级别不匹配将阻止任何访问,无论经典访问权限列表 (ACL) 设置为什么。跨度>
  • AFAIK 这只是匿名管道的问题。
  • 其实现由 A.Bouchez here 发布。我已经测试相同的代码很长时间了(here is 我的步骤之一)但它对我不起作用。是的,我有不同的情况;我试图通过本地网络将 VMWare 的机器连接到物理机器,但没有机会。还;安全属性和描述符已正确设置。
  • 我忘了提到我无法写入管道。阅读效果很好。
【解决方案2】:

当我们将产品从 Win 2K 迁移到 Win7 时,我们运行命名管道停止工作。在与 MS 交谈 2 周后(以及 275 美元),我们发现这是由使用共享文件夹文件设置引起的。取消选中此功能允许我们继续使用管道。

【讨论】:

    【解决方案3】:

    我试图实现这个:

    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;
    

    它似乎在服务器端工作(即安全属性按预期创建),您必须编写客户端代码,不要忘记在 SYSTEM\CurrentControlSet\Services\ 中添加管道名称lanmanserver\parameters\NullSessionPipes 注册表项,如预期的那样。

    【讨论】:

    • @Arnaud - 不会将安全属性设置为nil 并运行应用程序As administrator 做同样的事情(我的意思是完全访问)?这个hack并不为人所知,我敢打赌一定有一个干净的方法。通过网络可以,但在同一台计算机上,仅在不同的会话上?
    • @daemon 只需设置 nil 即可在同一台计算机上进行同一会话。也许在具有相同用户权限的会话上。但它无法通过网络在 Vista/7 上运行。
    • @Arnaud - 这是否意味着如果没有这段代码,我就无法建立从服务(会话#0)到客户端(会话#1)的管道?有了这个安全,我的意思是最坏的情况,当你不是管理员并且你在没有管理员提升的情况下运行你的应用程序(服务和客户端部分)?
    【解决方案4】:

    我似乎记得 RemObjects 在他们的包中有一个命名管道客户端/服务器控件。除非您的预算有限,否则我强烈建议您查看此类成品组件。正确处理既费时又棘手。

    另外,Justin Smyth 现在有一篇关于命名管道的文章。在此处查看他关于该主题的博客:http://smythconsulting.blogspot.com/2011/07/smartmediaplayer-pipes-part4.html

    祝你好运!

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,刚刚解决了。对我来说,它不起作用的原因是因为 Russels TPipe implementationetion 在创建管道之前检查了线程 ID:if not(Sync.SyncBaseTID = FNotifyThread) then..

      原来我在服务的错误位置创建了TPipeServer。 (我覆盖了DoStart 等而不是使用事件OnStart...不要那样做!) 我现在在同一个线程中创建 TPipeServer 实例,我稍后会在其中激活它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多