Unix Socket Credential Passing in Python

Unix socket credential passing 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.

Determine user connecting a local socket with Python

On Linux and other unixy system, you can use the ident service.

I'm not sure if Windows offers something similar.

Use socket to send username and password to tcp server

Firstly, you don’t need the colons after USER, PASS, SEND and END.

Also, the challenge tells you you need to receive data after every send.

Finally, you need to send the keywords and the data separately

So:

import socket

clientsocket = ...
clientsocket.connect(...)

clientsocket.send(‘USER’)
print(clientsocket.recv(1024))
clientsocket.send(‘aliensignal’)
print(clientsocket.recv(1024))
#etc...

Is there a way to get the uid of the other end of a unix socket connection

The easiest way to check peer credentials is with SO_PEERCRED.
To do this for socket sock:

int len;
struct ucred ucred;

len = sizeof(struct ucred);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
// check errno

printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
(long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
SO_PEERCRED
Return the credentials of the foreign process connected to
this socket. This is possible only for connected AF_UNIX
stream sockets and AF_UNIX stream and datagram socket pairs
created using socketpair(2); see unix(7). The returned
credentials are those that were in effect at the time of the
call to connect(2) or socketpair(2). The argument is a ucred
structure; define the _GNU_SOURCE feature test macro to obtain
the definition of that structure from <sys/socket.h>. This
socket option is read-only.

From a tlpi example. PostgreSQL has a few variants for other unices.

Client authorization with python backend

Don't reinvent the wheel. ;) I found this discussion enlightening:

  • http://cyberelk.net/tim/2007/03/08/cups-unix-domain-sockets-authentication/ Explains the concept of socket auth.
  • http://pythonic.pocoo.org/2007/7/7/unix-socket-credentials-with-python Helpful details.
  • http://atlee.ca/software/pam/ This might work also, though less convenient.
  • Create a group for app admins only, etc.

SO_PEERCRED vs SCM_CREDENTIALS - why there are both of them?

  1. If I understand correctly, there is a subtle difference between the two. SO_PEERCRED retrieves the credentials of the peer process, without requiring any interaction from the peer process. In contrast, SCM_CREDENTIALS is a mechanism to send / receive credentials of the peer process, which are then checked by the kernel. This subtle difference may matter when a process is running as UID 0. SCM_CREDENTIALS allows a process running as UID 0, to declare itself less privileged (e.g., UID 50), whereas this would not be possible with SO_PEERCRED.

  2. See above. I guess using SCM_CREDENTIALS is encouraged and SO_PEERCRED is only supported for compatibility.

  3. The dbus daemon seems to use SO_PEERCRED and getpeereid(). I think it is best to copy their code in order to portably get the credentials.

http://cgit.freedesktop.org/dbus/dbus/tree/dbus/dbus-sysdeps-unix.c?id=edaa6fe253782dda959d78396b43e9fd71ea77e3

Efficient way to send results every 1-30 seconds from one machine to another

Main thing here is to decide on a connection design and to choose protocol. I.e. will you have a persistent connection to your server or connect each time when new data is ready to it.

Then will you use HTTP POST or Web Sockets or ordinary sockets. Will you rely exclusively on nginx or your data catcher will be another serving service.

This would be a most secure way, if other people will be connecting to nginx to view sites etc.

Write or use another server to run on another port. For example, another nginx process just for that. Then use SSL (i.e. HTTPS) with basic authentication to prevent anyone else from abusing the connection.

Then on client side, make a packet every x seconds of all data (pickle.dumps() or json or something), then connect to your port with your credentials and pass the packet.

Python script may wait for it there.

Or you write a socket server from scratch in Python (not extra hard) to wait for your packets.
The caveat here is that you have to implement your protocol and security. But you gain some other benefits. Much more easier to maintain persistent connection if you desire or need to. I don't think it is necessary though and it can become bulky to code break recovery.
No, just wait on some port for a connection. Client must clearly identify itself (else you instantly drop the connection), it must prove that it talks your protocol and then send the data.
Use SSL sockets to do it so that you don't have to implement encryption yourself to preserve authentication data. You may even rely only upon in advance built keys for security and then pass only data.

Do not worry about the speed. Sockets are handled by OS and if you are on Unix-like system you may connect as many times you want in as little time interval you need. Nothing short of DoS attack won't inpact it much.

If on Windows, better use some finished server because Windows sometimes do not release a socket on time so you will be forced to wait or do some hackery to avoid this unfortunate behaviour (non blocking sockets and reuse addr and then some flo control will be needed).

As far as your data is small you don't have to worry much about the server protocol. I would use HTTPS myself, but I would write myown light-weight server in Python or modify and run one of examples from internet. That's me though.



Related Topics



Leave a reply



Submit