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 ()