Using abstract namespace Unix domain sockets and SO_PEERCRED
in Python
Linux has a special version of Unix domain sockets where the socket
address is not a socket file in the filesystem but instead in an
abstract namespace. It's possible
to use them from Python without particular problems, including
checking permissions with SO_PEERCRED
, but it's not completely
obvious how.
(For general information on using Unix domain sockets from Python, see UnixDomainSockets.)
With a normal Unix domain socket, the address you give is the path
to a socket file. Per the Linux unix(7)
manpage, an abstract
socket address is simply your abstract name with a 0 byte on the
front. This is trivial in Python and works exactly as you'd hope:
import sockets = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.bind("\0" + sname) s.listen(10) # or s.connect(...) to talk to a server ....
This works in both Python 2 and Python 3. Somewhat to my surprise,
Python 3 converts the Unicode null 'byte' codepoint to a 0 byte
without complaints. How Python 3 converts any non-ASCII in sname
to bytes depends on your locale, as usual, which means that under
some circumstances you may need to do explicit conversion to bytes
and handle conversion errors. You can call .bind()
or .connect()
with a bytes
address instead of a Unicode one.
Sockets in the abstract namespace have no permissions, unlike regular
Unix domain sockets (which are protected by file and/or directory
permissions). If you want to add a permissions system, you can
obtain the UID, GID, and PID of the other end with SO_PEERCRED
like so:
import structSO_PEERCRED = getattr(socket, "SO_PEERCRED", 17)creds = s.getsockopt(socket.SOL_SOCKET, SO_PEERCRED, struct.calcsize("3i")) pid, uid, gid = struct.unpack("3i", creds)
This comes from a 2011 Stackoverflow answer, more or less (I have added my own little modifications to it).
The situation with the definition for SO_PEERCRED
turns out to
be a little bit complicated. The Python 3 socket module has had a
definition for it for some time (it looks like since 2011 or so).
Most versions of Python 2.x don't have a SO_PEERCRED
constant
defined in the socket module; the exception is the Fedora version
of Python, which apparently has had this patched in for a very
long time now.
In addition, the '17' here is only correct on mainstream Linux
architectures; some oddball ones like MIPS have other values. You may
have to check in Python 3 or compile a little C program to get the
correct value. Yes, this is irritating and you can see why the Fedora
people patched Python (and why it got added to Python 3).
As you might suspect, SO_PEERCRED
can be used by either end of
a Unix domain socket connection (and it works on any Unix domain
socket, not just ones in the abstract namespace). It's merely most
useful for a server to find out what the client is, since clients
usually trust servers.
(Trusting the server may or may not be wise when you're dealing with Unix domain sockets in the abstract namespace, since anyone can grab any name in it. For my purposes I don't really care; my use is a petty little hack on my own personal machine and it doesn't involve anything sensitive.)
Comments on this page:
|
|