简体   繁体   中英

Unix socket credential passing in Python

How is Unix socket credential passing accomplished in Python?

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.

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM