13

How is Unix socket credential passing accomplished in Python?

2 Answers 2

25

Internet searches on this topic came up with surprisingly few results. I figured I'd post the question and answer here for others interested in this topic.

The following client and server applications demonstrate how to accomplish this on Linux with the standard python interpreter. No extensions are required but, due to the use of embedded constants, the code is Linux-specific.

Server:

#!/usr/bin/env python

import struct
from socket import socket, AF_UNIX, SOCK_STREAM, SOL_SOCKET

SO_PEERCRED = 17 # Pulled from /usr/include/asm-generic/socket.h

s = socket(AF_UNIX, SOCK_STREAM)

s.bind('/tmp/pass_cred')
s.listen(1)

conn, addr = s.accept()

creds = conn.getsockopt(SOL_SOCKET, SO_PEERCRED, struct.calcsize('3i'))

pid, uid, gid = struct.unpack('3i',creds)

print 'pid: %d, uid: %d, gid %d' % (pid, uid, gid)

Client:

#!/usr/bin/env python

from socket import socket, AF_UNIX, SOCK_STREAM, SOL_SOCKET

SO_PASSCRED = 16 # Pulled from /usr/include/asm-generic/socket.h

s = socket(AF_UNIX, SOCK_STREAM)

s.setsockopt(SOL_SOCKET, SO_PASSCRED, 1)

s.connect('/tmp/pass_cred')

s.close()

Unfortunately, the SO_PEERCRED and SO_PASSCRED constants are not exported by python's socket module so they must be entered by hand. Although these value are unlikely to change it is possible. This should be considered by any applications using this approach.

Sign up to request clarification or add additional context in comments.

3 Comments

I don't think they can ever change. In this case, all existing applications would have to be recompiled... But they might be different amongst several OSes or several platforms.
I'd +1 you but for one thing, this isn't actually credential passing. SO_PASSCRED allows receiving (not sending) of credentials, but those are sent using sendmsg and an ancillarry SCM_CREDENTIALS message. you are reading SO_PEERCRED which does not depend on this, only that the socket is a unix domain socket or was created by socketpair. and only gets the current credentials (root can send credential messages matching other users)
I think SO_PEERCRED and SO_PASSCRED are in the socket module right now (at least in 3.3). Didn't found when they were added. I did remember they were not there when I first find this post one year ago...
2

Here is a Python 3 version of Rakis' server.py that does not use hardcoded IDs and also displays user and group names of the client:

#!/usr/bin/env python
import os
import atexit
import grp
import pwd
import struct
from socket import socket, AF_UNIX, SOCK_STREAM, SOL_SOCKET, SO_PEERCRED

def remove_sock():
    try:
        os.unlink('/tmp/pass_cred')
    except FileNotFoundError:
        pass

remove_sock()
atexit.register(remove_sock)

s = socket(AF_UNIX, SOCK_STREAM)
s.bind('/tmp/pass_cred')
s.listen(1)

conn, addr = s.accept()

creds = conn.getsockopt(SOL_SOCKET, SO_PEERCRED, struct.calcsize('3i'))

pid, uid, gid = struct.unpack('3i',creds)

user_name = pwd.getpwuid(uid)[0]
group_name = grp.getgrgid(gid)[0]

print(f'pid={pid}, uid={uid}({user_name}), gid={gid}({group_name})')

Also updated to make reusable (you cannot bind the socket to a filename that already exists, so we clean the socket both before bind and after normal process exit)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.