marcs notes on struct ucred and SO_PEERCRED

Deep within the Linux system call API there lives a magic socket option. It is called SO_PEERCRED. Few people seem to know about it, which is a shame because it is really useful

For the impatient, the call looks like this

getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);

So what does that do ? The getsockopt system call is used to query socket file descriptors. getsockopt knows about various levels, the level needed here is SOL_SOCKET

The SO_PEERCRED option applies to sockets which belong to the unix domain. Unix domain sockets are a slightly obscure socket family, distinct from IP sockets or IPv6 sockets. Unix domain sockets are local to a system and are visible in the file system.

When getsockopt is invoked with the SO_PEERCRED option, it accepts the struct ucred structure as further parameter (cr). This structure has three elements: pid, uid and gid. After calling getsockopt with a pointer to this structure, these fields will contain the process, user and group ids of the party on the other end of the socket connection

What makes this call so important is that provides a clean and reliable way of establishing the identity of another process. Used smartly it can be used to replace a number setuid helper applications

For example: cron, print and mail servers need to provide a way for local users to queue jobs/messages in a controlled way - in the case of mail spoolers, some user classes might be permitted to change their sending identity while others should not be able to forge these headers. Historically, the way of handling this involves a setuid application which uses getuid and geteuid to establish who the invoking user is

Given that setuid binaries are difficult to secure, an approach involving SO_PEERCRED would make for a good replacement. Using this approach

  1. a client connects to the mail or cron server via a unix domain socket
  2. the server issues a SO_PEERCRED getsockopt call
  3. the results returned by the kernel allow the server to perform its access control decisions

A mechanism using SO_PEERCRED could also be a good way to replace conventional lock or pid files. pid files can be found in /var/run and are supposed to contain the pid of the process which is currently offering a particular service

The problem with pid files is that the can be stale. Often this is harmless but on occasion this can cause trouble

Consider a server process crash (maybe caused by segmentation fault). This then leaves a stale pid file. Over time pids are recycled, so that the pid file may end up pointing a completely different process. When the sysadmin finally notices the crash and restarts the server process, an unrelated process may get killed accidentally

Instead of creating a regular pid file, server processes can create a unix domain socket in its place. To check if a server is running, one can connect to the socket and retrieve the process id. In case the process has crashed, the connect will fail, so there is no risk of a stale lock file, provided the server process uses a temporary name and rename to create new pid socket files atomically

There are a number of other applications where SO_PEERCRED can be used to de-uglify logic or make systems more secure. For example, system loggers can use SO_PEERCRED to record the uid of each log message and ensure that one process or user can't consume all log space or forge records

Of course SO_PEERCRED is not without limitations of its own - it only works on unix domain sockets, these are sockets which look like files - in directory listings their mode/permission bits start with an s

srw-rw-rw- 1 root root 0 May 21 00:00 /tmp/.X11-unix/X0

Furthermore, the sockets have to be streamed, they can not be connectionless. Use netstat -x to check what STREAM are in use

To illustrate how peercred can be used, I have written a small example utility (list peercred) which will issue a getsockopt SO_PEERCRED on its first argument. Download lspc.tar.gz

On some systems the SO_PEERCRED define and struct ucred are not available by default, you may have to set __USE_GNU.

up to notes index