chapter 3. linux和unix安全特性概要
套接字用于通信,特别是在网络上。套接字起初是由unix系统的bsd分支开发出来的,但现在一般可以移植到其它类unix系统上:linux和system v的变种同样支持套接字,而且支持套接字是open group的单一unix规格[open group 1997]所要求的。system v系统传统上使用一个不同(不兼容)的网络通信接口,但这对像solaris这样包括对套接字支持的系统来说不值一提。socket(2)创建一个通信端点并返回一个描述符,与open(2)对文件的操作相似。套接字的参数指定协议族和类型,例如internet域(tcp/ipv4)、novell的ipx或“unix域”。服务器程序一般调用bind(2)、listen(2)和accept(2)或select(2),客户程序一般调用bind(2)(虽然可能被省略)和connect(2)。参见这些例程相应的man帮助页以了解更多信息。通过相应的man帮助页可能很难理解如何使用套接字;也许需要参考hall的“beej”[1999]一类的文献来学习如果共同使用这些调用。
“unix域套接字”实际上并不代表一个网络协议;它们只能与同一台机器上的套接字相连接。(在为标准linux内核编写本文的目前情况下)。在被作为流使用时,它们与命名管道非常相似,而优越性很明显。特别是unix域套接字面向连接;每一个到套接字的新连接都产生一个新的通信管道,这与命名管道完全不同。正是由于这一特性,unix域套接字经常被用来代替命名管道实现很多重要服务中的ipc。就像可以拥有非命名管道一样,可以用socketpair(2)来得到非命名unix域套接字;与非命名管道类似,非命名unix域套接字对于ipc也很有用。
unix域套接字有几个有趣的安全内涵。首先,虽然unix域套接字可以出现在文件系统中,而且可以对它们使用stat(2),却不能用open(2)打开它们(只能使用socket(2)和友好接口)。其次,unix域套接字可以用来在进程间传递文件描述符(而不仅仅是文件的内容)。其它ipc机制都不提供的这一奇特能力被用来破解所有规范(描述符基本上可以用作受限制版本的计算机科学意义上的“能力”)。文件描述符用sendmsg(2)发送,其中msg(消息)的msg_control域指向一个控制消息头的数组(msg_controllen域必须指定数组中所包含的字节数目)。每条控制消息都是一个带有数据的cmsghdr结构,为达到此目的需要把cmsg_type设置为scm_rights。文件描述符通过recvmsg(2)获得,然后以相似的方式传递下去。坦白地说,该特性风格有些绮靡,但值得了解。
linux 2.2支持unix域套接字的一个附加特性:可以获取对端的“可信任证明”(pid、uid和gid)。下面是一段代码示例: /* fd= file descriptor of unix domain socket connected
to the client you wish to identify */
struct ucred cr;
int cl=sizeof(cr);
if (getsockopt(fd, sol_socket, so_peercred, &cr, &cl)==0) {
printf(\"peer\'s pid=%d, uid=%d, gid=%d\\n\",
cr.pid, cr.uid, cr.gid);
标准的unix约定是需要root权限来绑定小于1024的数字作为tcp和udp本地端口号,而任何进程都可以绑定一个大于或等于1024的不受约束的端口号。linux遵循了这一约定,更具体地说,要绑定小于1024的端口号,linux要求进程具有cap_net_bind_service能力;一般此能力只有euid为0的进程才拥有。想进一步了解的读者可以查看linux下相应的源码;在linux 2.2.12中为文件/usr/src/linux/net/ipv4/af_inet.c中的函数inet_bind()。