TCP SOCKET 句柄可以设置为不可继承吗? [英] Can TCP SOCKET handles be set not inheritable?

查看:26
本文介绍了TCP SOCKET 句柄可以设置为不可继承吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Windows 句柄可以设置为可继承或不可继承,以控制子进程是否接收它们(当 CreateProcess 中的 bInheritHandles 为 TRUE 时).但是,使用 SetHandleInformation 标记 SOCKET 不可继承并不总是有效.特别是,当安装了某些分层服务提供程序 (LSP) 时,子进程无论如何都会继承句柄.这特别容易导致侦听套接字出现错误.(但是,由于另一个问题,如果孩子尝试使用套接字,它将无法使用!真正的问题-22!)

重现步骤

  1. 例如创建一个监听套接字.使用 SetHandleInformation 将其标记为不可继承.
  2. 生成一个孩子,bInheritHandles 为真.
  3. 关闭父级中的套接字,并尝试重新绑定到端口.

当安装了(非 IFS)LSP 时,例如.PCTools Internet Security,侦听套接字将在子进程中打开(在 netstat 中可见),尽管在创建子进程之前在套接字上调用了 SetHandleInformation 以禁用继承.>

有关替代方案,请参阅KB2398202中的(简要)步骤.

有哪些解决方法?

解决方案

简短回答

通常不可能将 SOCKET 句柄设置为不可继承.也就是说,当安装了某些(非 IFS)LSP 时,即使您将进程中的句柄标记为不可继承,也不可能使用 bInheritHandles=TRUE 来阻止子进程接收它们.

说明

防火墙或 A/V 产品通常使用 LSP 来过滤所有 TCP 连接.LSP 是由 WinSock 加载到您的进程中的 DLL,它处理所有 TCP 操作,通常通过执行一些过滤然后将调用直接传递到底层 WinSock 实现.LSP 通过为 WinSock 实现产生的每个实际 SOCKET 句柄创建一个虚拟句柄来工作:您对 WSASocket 的调用将为您提供虚拟句柄;当您使用虚拟句柄时,调用将发送到创建它的 LSP;然后 LSP 将哑元映射回实际句柄,并将操作(例如 acceptbind)传递给底层句柄.

因此,问题在于在您创建的套接字上调用 SetHandleInformation 是不够的:您从未见过的底层句柄(由 LSP 内部使用)仍然由子进程继承.

解决方法

  1. 永远不要调用 CreateProcess 允许从使用套接字的应用程序进行任何继承.这是最可靠的解决方案.相反,要与孩子建立通信,请创建一个具有适当权限的命名管道,在命令行上将其名称传递给孩子,然后在孩子中重新连接.然后手动传递您希望孩子继承的任何句柄.这是安全的,因为尽管其他用户可以读取命令行,但如果正确设置,只有孩子的实际用户令牌才能连接到管道.
    如果您只想做一些简单的事情,例如重定向孩子的 stdio,这是非常不优雅的,因为您必须控制孩子中的参数解析.要解决此问题,请创建一个包装二进制文件,该二进制文件从命令行读取命名管道名称并进行连接,设置句柄可继承,并使用重定向的 stdio 重新调用其余参数.从包装器继承句柄是安全的,因为进程中没有套接字.
  2. 或者,在 Vista(带有 KB2398202)、Windows 7(带有 SP1)和更高版本上,WSA_FLAG_NO_HANDLE_INHERIT 标志被添加到 WSASocket.(这是我从 Microsoft 找到的关于问题的尽可能多的文档.创建修补程序几乎是承认,如果没有它,就不可能阻止基本服务提供程序句柄被继承.虽然它没有得到很好的宣传!)
  3. 最后,在 Vista 上,还有 ioctl 允许查询基本服务提供者使用的句柄.然后可以将这些标记为不可继承.虽然这很痛苦,但仍然无法解决 XP 问题.

Windows handles can be set to be either inheritable or not, to control whether child processes will receive them (when bInheritHandles in CreateProcess is TRUE). However, using SetHandleInformation to mark a SOCKET non-inheritable does not always work. In particular, when certain Layered Service Providers (LSPs) are installed, the child process inherits the handle anyway. This is particularly likely to cause bugs with listening sockets. (But, because of another issue, if the child were to try using the socket, it would not be able! A real catch-22!)

Steps to reproduce

  1. Create eg a listening socket. Mark it non-inheritable using SetHandleInformation.
  2. Spawn a child, with bInheritHandles true.
  3. Close the socket in the parent, and attempt to re-bind to the port.

When a (non-IFS) LSP is installed, eg. PCTools Internet Security, the listening socket will be open in the child (visible in netstat), despite SetHandleInformation being called on the socket to disable inheritance before creating the child.

For an alternative scenario, see the (brief) steps in KB2398202.

What workarounds are available?

解决方案

Short answer

It is not possible in general to set SOCKET handles non-inheritable. That is, when certain (non-IFS) LSPs are installed, even if you mark handles in your process specifically non-inheritable, it is not possible to stop a child process with bInheritHandles=TRUE from receiving them.

Explanation

LSPs are commonly used by firewall or A/V products to filter all TCP connections. The LSP is a DLL loaded into your process by WinSock which handles all the TCP operations, typically by performing some filtering and then passing the call straight down to the underlying WinSock implementation. The LSP works by creating a dummy handle for each actual SOCKET handle produced the WinSock implementation: your call to WSASocket will give you the dummy handle; when you use the dummy handle, the call is sent to the LSP which created it; the LSP then maps the dummy back to the actual handle, and passes on the operation (such as accept or bind) to the underlying handle.

The problem is therefore that calling SetHandleInformation on the sockets you create is not enough: the underlying handle that you never see (used internally by the LSP) is still inherited by child processes.

Workarounds

  1. Don't ever call CreateProcess allowing any inheritance from an application which uses sockets. This is the most reliable solution. Instead, to set up a communication with the child, create a named pipe with suitable permissions, pass its name on the commandline to the child, and connect back in the child. Then manually pass over any handles you wish the child to inherit. This is secure, since although the commandline may be readable by other users, only the actual user token of the child can connect to the pipe if it's set up correctly.
    This is extremely inelegant if all you want to do is something simple like redirect a child's stdio, since you have to control the argument parsing in the child. To work around that, create a wrapper binary which reads the named pipe name from the commandline and connects, sets the handle inheritable, and reinvokes the remaining arguments with stdio redirected. It is safe to inherit handles from the wrapper, because there are no sockets in the process.
  2. Alternatively, on Vista (with KB2398202), Windows 7 (with SP1), and later, the WSA_FLAG_NO_HANDLE_INHERIT flag was added to WSASocket. (This is as much documentation of the problem as I can find from Microsoft. Creating the hotfix is pretty much acknowledgement that it's not possible without it to prevent the Base Service Provider handles from being inherited. It's not well advertised though!)
  3. Finally, on Vista, there are also ioctls which allow the handles used by a Base Service Provider to be queried. These can then be marked non-inheritable. This is a pain though, and still doesn't fix things for XP.

这篇关于TCP SOCKET 句柄可以设置为不可继承吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆