unix domain socket进程凭据

进程凭据是指unix domain socket(AF_UNIX)发送方的pid,uid,gid信息。

只能是AF_UNIX,不能是AF_INET的原因很简单,AF_INET可能都不在同一台机器上,pid,uid,gid没有意义。

在以下的内容中,socket server作为接收方,socket client作为发送方,当然反过来也没有问题,不过本文以这个为例。

有两种方法传递进程凭据:

1、SO_PEERCRED

man pages中的解释:

 

SO_PEERCRED
              Return the credentials of the foreign process connected to
              this socket.  This is possible only for connected AF_UNIX
              stream sockets and AF_UNIX stream and datagram socket pairs
              created using socketpair(2); see unix(7).  The returned
              credentials are those that were in effect at the time of the
              call to connect(2) or socketpair(2).  The argument is a ucred
              structure; define the _GNU_SOURCE feature test macro to obtain
              the definition of that structure from <sys/socket.h>.  This
              socket option is read-only.

 

在socket server端调用如下代码:

 

struct ucred cred;
socklen_t len;
len = sizeof(struct ucred);
// ......, after accept
getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED, &cred, &len);
printf("Credentials from SO_PEERCRED: pid=%d, uid=%d, gid=%d\n", cred.pid, cred.uid, cred.gid);

注意编译时先#define _GNU_SOURCE,再#include <sys/socket.h>,否则struct ucred的定义找不到的;

 

需要对client_fd调用getsockopt,如果对listen_fd调用的话,每次都是socket server自己的pid,uid,gid,没啥用处;

得到的pid,uid,gid是socket client在connect或者socketpair时的值;

在socket client端无需特殊的操作,也无需发送消息数据。

 

2、SO_PASSCRED + SCM_CREDENTIALS

man pages上的解释:

 

SO_PASSCRED
              Enables the receiving of the credentials of the sending
              process in an ancillary message.  When this option is set and
              the socket is not yet connected a unique name in the abstract
              namespace will be generated automatically.  Expects an integer
              boolean flag.
SCM_CREDENTIALS
              Send or receive UNIX credentials.  This can be used for
              authentication.  The credentials are passed as a struct ucred
              ancillary message.  Thus structure is defined in
              <sys/socket.h> as follows:

                  struct ucred {
                      pid_t pid;    /* process ID of the sending process */
                      uid_t uid;    /* user ID of the sending process */
                      gid_t gid;    /* group ID of the sending process */
                  };

              Since glibc 2.8, the _GNU_SOURCE feature test macro must be
              defined (before including any header files) in order to obtain
              the definition of this structure.

              The credentials which the sender specifies are checked by the
              kernel.  A process with effective user ID 0 is allowed to
              specify values that do not match its own.  The sender must
              specify its own process ID (unless it has the capability
              CAP_SYS_ADMIN), its user ID, effective user ID, or saved set-
              user-ID (unless it has CAP_SETUID), and its group ID,
              effective group ID, or saved set-group-ID (unless it has
              CAP_SETGID).  To receive a struct ucred message the
              SO_PASSCRED option must be enabled on the socket.

socket client同样无需特殊的操作,不过需要sendmsg之后,接收端才能够得到凭据,凭据数据由内核填充到消息结构体的控制数据中,如果发送方想自己填充也可以,伪造的数据会导致sendmsg失败。除非有root权限,才可能伪造pid,uid,gid信息。

 

socket client构建消息结构体的代码为:

 

// 权限数据由内核填充还是程序填充
#define AUTO_FILL_DATA

    struct msghdr msgh;
    struct iovec iov;
    int data = 0xbeef;
#ifndef AUTO_FILL_DATA
    union {
        struct cmsghdr cmh;
        char   control[CMSG_SPACE(sizeof(struct ucred))];
                        /* Space large enough to hold a ucred structure */
    } control_un;
    struct cmsghdr *cmhp;
    struct ucred *ucp;
#endif

    /* On Linux, we must transmit at least 1 byte of real data in
       order to send ancillary data */
    msgh.msg_iov = &iov;
    msgh.msg_iovlen = 1;
    iov.iov_base = &data;
    iov.iov_len = sizeof(int);
    msgh.msg_name = NULL;
    msgh.msg_namelen = 0;
#ifdef AUTO_FILL_DATA
    msgh.msg_control = NULL;
    msgh.msg_controllen = 0;
#else
    msgh.msg_control = control_un.control;
    msgh.msg_controllen = sizeof(control_un.control);
    cmhp = CMSG_FIRSTHDR(&msgh);
    cmhp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
    cmhp->cmsg_level = SOL_SOCKET;
    cmhp->cmsg_type = SCM_CREDENTIALS;
    ucp = (struct ucred *) CMSG_DATA(cmhp);
    ucp->pid = getpid();
    ucp->uid = getuid();
    ucp->gid = getgid();
#endif

socket server端,需要设置期望接收的消息的结构体:

 

    struct msghdr msgh;
    struct iovec iov;
    int data;
    struct ucred *ucredp;
    struct cmsghdr *cmhp;
    union {
        struct cmsghdr cmh;
        char   control[CMSG_SPACE(sizeof(struct ucred))];
                        /* Space large enough to hold a ucred structure */
    } control_un;

    control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred));
    control_un.cmh.cmsg_level = SOL_SOCKET;
    control_un.cmh.cmsg_type = SCM_CREDENTIALS;
    msgh.msg_control = control_un.control;
    msgh.msg_controllen = sizeof(control_un.control);
    msgh.msg_iov = &iov;
    msgh.msg_iovlen = 1;
    iov.iov_base = &data;
    iov.iov_len = sizeof(int);
    msgh.msg_name = NULL;
    msgh.msg_namelen = 0;

在accept之后,对client_fd调用:

 

setsockopt(client_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval))

其中int optval = 1;

 

然后使用如下代码接收消息,并获取控制信息,即凭据:

 

    if (recvmsg(client_fd, &msgh, 0) <= 0)
    {
        printf("ERROR recvmsg\n");
        goto out;
    }
    cmhp = CMSG_FIRSTHDR(&msgh);
    if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
    {
        printf("bad cmsg header / message length");
        goto out;
    }
    if (cmhp->cmsg_level != SOL_SOCKET)
    {
        printf("cmsg_level != SOL_SOCKET");
        goto out;
    }
    if (cmhp->cmsg_type != SCM_CREDENTIALS)
    {
        printf("cmsg_type != SCM_CREDENTIALS");
        goto out;
    }

    ucredp = (struct ucred *) CMSG_DATA(cmhp);
    printf("Received credentials pid=%d, uid=%d, gid=%d\n",
                ucredp->pid, ucredp->uid, ucredp->gid);

和第一种方式不同,这里也可以在accept之前,对listen_fd调用

setsockopt(listen_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval))

来获取client_fd上的凭据。但是,这种方法不保险,有些内核是不支持的,比如android goldfish 3.4。

因为在accept的时候,client_fd没有从listen_fd继承相关的标志位,所以会不支持。

在内核中添加这个patch即可:http://patchwork.ozlabs.org/patch/289624/

参考:

https://sourceware.org/bugzilla/show_bug.cgi?id=6545

http://man7.org/tlpi/code/online/dist/sockets/scm_cred_recv.c.html
http://man7.org/tlpi/code/online/dist/sockets/scm_cred_send.c.html

时间: 2024-08-13 03:26:41

unix domain socket进程凭据的相关文章

(unix domain socket)使用udp发送&gt;=128K的消息会报ENOBUFS的错误

1.Unix domain socket简介 unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,所用API于在不同主机上执行客户/服务器通信所有的 API(套接字API,如AF_INET.AF_INET6等类型的API)相同.unix域协议可以视为是进程之间本地通信IPC的一种. unix域提供两类套接口:字节流套接口(类似TCP)和数据报套接口(类似UDP).使用Unix域套接口的理由有三: Unix域套接口往往比位于同一主机的TCP套接口快出一倍. Uni

运用异步输入输出流编写Socket进程通信

同步?异步输入输出机制的引入 在Merlin之前,编写Socket程序是比 较繁琐的工作.因为输入输出都必须同步.这样,对于多客户端客户/服务器模式, 不得不使用多线程.即为每个连接的客户都分配一个线程来处理输入输出.由此而 带来的问题是可想而知的.程序员不得不为了避免死锁,线程安全等问题,进行大 量的编码和测试.很多人都在抱怨为什么不在Java中引入异步输入输出机制.比较 官方的解释是,任何一种应用程序接口的引入,都必须兼容任何操作平台.因为 Java是跨平台的.而当时支持异步输入输出机制的操

android-Android开发监听跳转SOCKET进程的问题

问题描述 Android开发监听跳转SOCKET进程的问题 10C 本人写了一个程序,播放一个音频文件,如果点击xml文件的播放按钮,直接跳转到Socket进程,并把这个音频文件发送到指定服务器,请问这个跳转以及文件地址的传递该怎么写 解决方案 可以看看http://blog.csdn.net/junfeng120125/article/details/8187378 永不放弃的IT码农的博客文章 解决方案二: 唉??你上传文件为啥要用socket呢,为啥不用http上传呢?你的服务器不支持吗?

cron4j 2.2.4发布 UNIX cron守护进程

cron4j是个高度程序适用于Java 2平台,非常类似UNIX cron守护进程. cron4j 2.2.4该版本Processhttp://www.aliyun.com/zixun/aggregation/17034.html">Task类的一个错误被修复. 下载地址:https://sourceforge.net/projects/cron4j/files/cron4j/2.2.4/cron4j-2.2.4.zip/download

UNIX系统管理:进程控制

完成这一章,你能够做以下事情: 使用ps命令 在后台运行进程 ,并且使用ps命令监视正在运行的进程的状态 运行一个后台进程并使其在你退出系统后不被挂起. 切换后台进程到前台运行. 挂起一个进程. 停止进程的运行. 1 ps 命令 语法: ps [-efl] 报告进程的状态 例子: $ ps PID TTY TIME COMMAND 1324 ttyp2 0:00 sh 1387 ttyp2 0:00 ps $ ps –ef UID PID PPID C STIME TTY TIME COMMAN

linux网络编程之socket(十五) UNIX域套接字编程和socketpair 函数

一.UNIX Domain Socket IPC socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机 制,就是UNIX Domain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是 UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包.计算校验和.维护序号和应答等,只是 将应用层数据从一个进程拷贝到另一个进程.UNIX域套接字与TCP套接字相比较,

linux网络编程之socket(一) socket概述和字节序、地址转换函数

一.什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口. socket不仅可以用于本机的进 程间通信,还可以用于网络上不同主机的进程间通信. socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4.IPv6,以及以后要讲的UNIX Domain Socket.然而,各种网络协议的地址格式并不相同,如下图所示: IPv4和IPv6的地址格式定义在netinet/in.h 中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位

Socket编程实战

Socket 在英文中的含义为"(连接两个物品的)凹槽",像the eye socket,意为"眼窝",此外还有"插座"的意思.在计算机科学中,socket 通常是指一个连接的两个端点,这里的连接可以是同一机器上的,像unix domain socket,也可以是不同机器上的,像network socket. 本文着重介绍现在用的最多的 network socket,包括其在网络模型中的位置.API 的编程范式.常见错误等方面,最后用 Pytho

1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client

 1  Socket编程 socket这个词可以表示很多概念: 在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的一个进程,"IP 地址+端口号"就称为socket. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接.socket本身有"插座"的意思,因此用来描述网络连 接的一对一关系. TCP/IP协议最早在BSD UNIX上实现,