Identify Other End of a Unix Domain Socket Connection

Identify other end of a unix domain socket connection

Update: It's been possible to to do this using actual interfaces for a while now. Starting with Linux 3.3, the UNIX_DIAG feature provides a netlink-based API for this information, and lsof 4.89 and later support it. See https://unix.stackexchange.com/a/190606/1820 for more information.

Is there a way to get the uid of the other end of a unix socket connection

The easiest way to check peer credentials is with SO_PEERCRED.
To do this for socket sock:

int len;
struct ucred ucred;

len = sizeof(struct ucred);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
// check errno

printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
(long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
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.

From a tlpi example. PostgreSQL has a few variants for other unices.

Identify program that connects to a Unix Domain Socket

Yes, this is possible on Linux, but it won't be very portable. It's achieved using what is called "ancillary data" with sendmsg / recvmsg.

  • Use SO_PASSCRED with setsockopt
  • Use SCM_CREDENTIALS and the struct ucred structure

This structure is defined in Linux:

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 */
};

Note you have to fill these in your msghdr.control, and the kernel will check if they're correct.

The main portability hindrance is that this structure differs on other Unixes - for example on FreeBSD it's:

struct cmsgcred {
pid_t cmcred_pid; /* PID of sending process */
uid_t cmcred_uid; /* real UID of sending process */
uid_t cmcred_euid; /* effective UID of sending process */
gid_t cmcred_gid; /* real GID of sending process */
short cmcred_ngroups; /* number or groups */
gid_t cmcred_groups[CMGROUP_MAX]; /* groups */
};

how do I find out what program's on the other end of a local socket?

ss -p

will tell. (Provided the socket is not owned by the kernel itself.)

How do Unix Domain Sockets differentiate between multiple clients?

If you create a PF_UNIX socket of type SOCK_STREAM, and accept connections on it, then each time you accept a connection, you get a new file descriptor (as the return value of the accept system call). This file descriptor reads data from and writes data to a file descriptor in the client process. Thus it works just like a TCP/IP connection.

There's no “unix domain protocol format”. There doesn't need to be, because a Unix-domain socket can't be connected to a peer over a network connection. In the kernel, the file descriptor representing your end of a SOCK_STREAM Unix-domain socket points to a data structure that tells the kernel which file descriptor is at the other end of the connection. When you write data to your file descriptor, the kernel looks up the file descriptor at the other end of the connection and appends the data to that other file descriptor's read buffer. The kernel doesn't need to put your data inside a packet with a header describing its destination.

For a SOCK_DGRAM socket, you have to tell the kernel the path of the socket that should receive your data, and it uses that to look up the file descriptor for that receiving socket.

If you bind a path to your client socket before you connect to the server socket (or before you send data if you're using SOCK_DGRAM), then the server process can get that path using getpeername (for SOCK_STREAM). For a SOCK_DGRAM, the receiving side can use recvfrom to get the path of the sending socket.

If you don't bind a path, then the receiving process can't get an id that uniquely identifies the peer. At least, not on the Linux kernel I'm running (2.6.18-238.19.1.el5).

Is it possible to find which user is at the other end of a localhost TCP connection?

On Linux, /proc/net/tcp contains information on the open TCP sockets on the system. For a connected socket, the entries look like this (the header is part of the file, other lines removed):

  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     

11: 0100007F:C9CB 0100007F:0016 01 00000000:00000000 00:00000000 00000000 1000 0 978132 ...

The second and third columns have the endpoints of the socket, and the uid column has the effective UID of the process what created the socket.
/proc/net/tcp6 is similar for IPv6. (The IP address there is 127.0.0.1, so the octets seem to be in reverse order.)

If you wanted to track the actual process(es) holding the socket, you'd need to go through all /proc/$PID/fd/$N entries, and compare the inode numbers in the socket symlinks to the inode number mentioned in the tcp socket table. But you can only see the file descriptors of your own processes, unless you're the superuser.



Related Topics



Leave a reply



Submit