Unix Domain Sockets



I recently had to find a solution for a communication problem: An application running on a web-server should update configuration files that are only readable by a privileged user and these should not be directly writeable by the web-server user.
So the idea was to write an update-server running under the privileged account which receives update requests (and can perform additional checks) from the unprivileged web server user.
One of the checks I wanted to make was that only the web-server user (www-data on debian) should be able to send update requests. So I had to find out the user sending a request via the Unix-domain socket. Google found a nice socket howto on Henning Makholm’s blog which told me most of what I needed to know: “so I ended up just checking the EUID of the client process after the connection has been accept()ed. For your reference, the way to do this is getsockopt() with SO_PEERCRED for Linux”.
But one issue was remaining: I didn’t need a SOCK_STREAM socket but wanted to send datagrams to the other side (and didn’t want to fiddle with implementing my own datagram layer on top of a stream socket). With normal SOCK_DGRAM datagram sockets there is no connection — and therefore I can’t determine the user sending the datagram from the other side of the socket.
Looking further I discovered that Linux has connection-oriented datagram sockets for quite some time under the name SOCK_SEQPACKET. With this type of socket you first connect() to the other side and then you send a datagram. Since now there is a connection the trick with SO_PEERCRED as described above works, too.
Code for Server (python):


from socket import socket, SOCK_SEQPACKET, AF_UNIX, SOL_SOCKET
from struct import unpack
try :
    # Not implemented in python 2.6, maybe higher
    from socket import SO_PEERCRED
except ImportError :
    SO_PEERCRED = 17 # Linux
sock = socket (AF_UNIX, SOCK_SEQPACKET)
path = ‘/path/to/socket’
try :
    os.remove (path)
except OSError :
    pass
sock.bind (path)
conn, adr = self.sock.accept ()
ucred = conn.getsockopt (SOL_SOCKET, SO_PEERCRED, 12)
pid, uid, gid = unpack (‘LLL’, ucred)
if uid… check uid:
    error…
    conn.close ()

data = conn.recv (4096)

Code for client (python):


from socket import socket, SOCK_SEQPACKET, AF_UNIX
s = socket (AF_UNIX, SOCK_SEQPACKET)
s.connect (‘path/to/socket’)
s.send (…..)
s.close ()