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

目录
相关文章
|
网络协议 安全 Unix
UNIX域套接字(Unix Domain Socket,UDS)之所以高效
UNIX域套接字(Unix Domain Socket,UDS)之所以高效
705 3
|
监控 安全 Unix
UNIX域套接字(Unix Domain Socket)在安全性和隐私性
UNIX域套接字(Unix Domain Socket)在安全性和隐私性
433 2
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
Arthas 测试技术
这个错误提示表明Arthas无法打开目标进程的socket文
【1月更文挑战第11天】【1月更文挑战第55篇】这个错误提示表明Arthas无法打开目标进程的socket文
1324 4
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
695 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
Arthas 测试技术
错误提示表明Arthas无法打开目标进程的socket文件
错误提示表明Arthas无法打开目标进程的socket文件
156 2
|
监控 Unix Linux
socket监控进程,并对程序执行有关操作。
socket监控进程,并对程序执行有关操作。
106 0
|
4月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
4月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
213 5
|
12月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能